# Copyright (c) 2000, 2001, Red Hat, Inc.
# 
# This file is part of Source-Navigator.
# 
# Source-Navigator is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# 
# Source-Navigator is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with Source-Navigator; see the file COPYING.  If not, write to
# the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
# MA 02111-1307, USA.
# 
# misc.tcl - Miscellaneous routines. 
# Copyright (C) 1998 Cygnus Solutions.

# Put up splash graphic. Used for both splash screen and about box.
# type is either 'splash' or 'about'. It is up to the caller to catch errors
# generated by this routine. Returns the name of the window created.

# changed 03/2007 by Freek: randomly choose splash img

package require Itk

proc sn_splash_dialog {type} {
    global sn_options sn_product_version sn_bdb_version
    global sn_path sn_product_name sn_suite_name
    global tcl_platform sn_elix

    set w .${type}

    # randomly choose splash screen
    set splash_img splashsn
    append splash_img [expr {int(rand() * 4)}]
    append splash_img .gif

    sourcenav::Window ${w}
    wm withdraw ${w}

    ${w} configure -title [list ${sn_product_name}]
    sn_log "creating splash image [file join $sn_path(bitmapdir) $splash_img]"

    image create photo cyg-sol -file [file join $sn_path(bitmapdir) $splash_img] -palette 0/0/8

    ${w} configure -background white

    if {$tcl_platform(platform) == "windows"} {
        set font_large "Arial 10 bold"
        set font_small "Arial 8 bold"
        set font_super "Arial 6 bold"
    } else {
        set font_large [sn_make_font "" Bold 12]
        set font_small [sn_make_font "" Bold 10]
        set font_super [sn_make_font "" Bold 8]
    }
    label ${w}.logos -bg white -image cyg-sol
    text ${w}.text -bg white -relief flat -width 50 -highlightthickness 0\
      -font ${font_large} -wrap word
    ${w}.text tag configure large -font ${font_large}
    ${w}.text tag configure small -font ${font_small}
    ${w}.text tag configure super -font ${font_super} -offset 6
    ${w}.text tag configure center -justify center
    if {${sn_elix}} {
        ${w}.text insert end ${sn_suite_name} {center large}
        ${w}.text insert end "\n" {center large}
    }
    ${w}.text insert end ${sn_product_name} {center large}
    ${w}.text insert end "TM " {center super}
    ${w}.text insert end ${sn_product_version} {center large}
    ${w}.text insert end "\n" {center large}
    ${w}.text insert end "Berkeley DB version " {center large}
    ${w}.text insert end ${sn_bdb_version} {center large}
    ${w}.text insert end "\n\n" {center large}

    ${w}.text insert end [get_indep String SplashCopyrightText] {center large}
    ${w}.text insert end "\n"
    if {${type} == "about"} {
        ${w}.text insert end "\n\n" {center small}
        ${w}.text insert end [get_indep String SplashSubCopyrightText]\
          {center small}
        ${w}.text insert end "\n\n" {center small}
        ${w}.text insert end [get_indep String SplashAllRightsReserved]\
          {center small}

        # Make all of the TM strings superscript
        set search_index [${w}.text search -exact TM 0.0 end]
        while {${search_index} != {}} {
            ${w}.text tag add super ${search_index} "${search_index} +2 chars"
            set search_index [${w}.text index "${search_index} +2 chars"]
            set search_index [${w}.text search -exact TM ${search_index} end]
        }
    }

    set last_index [${w}.text index end]
    set num_lines [string range ${last_index} 0 [expr [string first\
      "." ${last_index}] - 1]]
    set num_lines [expr ${num_lines} + 3]
    ${w}.text configure -height ${num_lines}

    pack ${w}.logos -side top
    pack ${w}.text

    ${w}.text config -state disabled

    catch {${w} resizable no no}
    return ${w}
}

proc sn_show_version_window {} {

    set w [sn_splash_dialog about]
    sn_motif_buttons ${w} bottom 0 [get_indep String ok]
    ${w}.button_0 configure -command "
            destroy ${w}
            set tkPriv(button) -1
        "

    ${w} bind_tk <Return> "catch {${w}.button_0 invoke}"
    ${w} bind_tk <Escape> "catch {${w}.button_0 invoke}"

    catch {${w} grab set}
    ${w} move_to_mouse

    set tkPriv(button) ""
    tkwait variable tkPriv(button)
}

proc sn_show_splash_screen {} {
    global splash_start_time

    if {[winfo exists .splash]} {
        return
    }

    set splash_start_time [clock seconds]
    set w [sn_splash_dialog splash]
    wm overrideredirect ${w} 1
    ${w} config_tk -relief solid -borderwidth 2
    
    ${w} centerOnScreen
    ${w} deiconify
# FIXME: Do we really need this call to take_focus here?
    ${w} take_focus

    #hide the window after a reasonable time, don't
    #wait in active mode
    global splash_screen_open
    set splash_screen_open "open"
    after 1500 "destroy .splash; set splash_screen_open closed"
    tkwait variable splash_screen_open
}


proc find_reusable_window {pat {raise 1}} {
    foreach w [sn_get_windows ${pat}] {
        upvar #0 ${w}.reusable reuse
        if {${reuse}} {
            if {${raise}} {
                if {[wm state ${w}] != "normal"} {
                    wm deiconify ${w}
                } else {
                    raise ${w}
                }
            }
            return ${w}
        }
    }
    return ""
}

proc convert_scope_to_num {scope} {
    switch ${scope}\
          "all" {
                return [get_indep Pos All]
            }\
          "cl" {
                return [get_indep Pos Classes]
            }\
          "com" {
                return [get_indep Pos Commons]
            }\
          "con" {
                return [get_indep Pos Const]
            }\
          "cov" {
                return [get_indep Pos CommonsVars]
            }\
          "iu" {
                return [get_indep Pos Includes]
            }\
          "e" {
                return [get_indep Pos Enums]
            }\
          "ec" {
                return [get_indep Pos EnumCons]
            }\
          "f" {
                return [get_indep Pos Files]
            }\
          "fr" {
                return [get_indep Pos Friends]
            }\
          "fu" {
                return [get_indep Pos Functions]
            }\
          "fd" {
                return [get_indep Pos FunctionsDec]
            }\
          "gv" {
                return [get_indep Pos Variables]
            }\
          "iv" {
                return [get_indep Pos ClassVars]
            }\
          "iu" {
                return [get_indep Pos Includes]
            }\
          "lv" {
                return [get_indep Pos PafLocVars]
            }\
          "ma" {
                return [get_indep Pos Defines]
            }\
          "md" {
                return [get_indep Pos Methods]
            }\
          "mi" {
                return [get_indep Pos MethodImps]
            }\
          "su" {
                return [get_indep Pos Subr]
            }\
          "t" {
                return [get_indep Pos Typedefs]
            }\
          "ud" {
                return [get_indep Pos Undefined]
            }\
          "un" {
                return [get_indep Pos Unions]
            }
    return 0
}

proc convert_scope_to_str {scope} {
    switch ${scope}\
          "all" {
                return [get_indep String All]
            }\
          "cl" {
                return [get_indep String Classes]
            }\
          "com" {
                return [get_indep String Commons]
            }\
          "con" {
                return [get_indep String Const]
            }\
          "cov" {
                return [get_indep String CommonsVars]
            }\
          "iu" {
                return [get_indep String Includes]
            }\
          "e" {
                return [get_indep String Enums]
            }\
          "ec" {
                return [get_indep String EnumCons]
            }\
          "f" {
                return [get_indep String Files]
            }\
          "fr" {
                return [get_indep String Friends]
            }\
          "fu" {
                return [get_indep String Functions]
            }\
          "fd" {
                return [get_indep String FunctionsDec]
            }\
          "gv" {
                return [get_indep String Variables]
            }\
          "iv" {
                return [get_indep String ClassVars]
            }\
          "iu" {
                return [get_indep String Includes]
            }\
          "lv" {
                return [get_indep String PafLocVars]
            }\
          "ma" {
                return [get_indep String Defines]
            }\
          "md" {
                return [get_indep String Methods]
            }\
          "mi" {
                return [get_indep String MethodImps]
            }\
          "su" {
                return [get_indep String Subr]
            }\
          "t" {
                return [get_indep String Typedefs]
            }\
          "ud" {
                return [get_indep String Undefined]
            }\
          "un" {
                return [get_indep String Unions]
            }
    return ""
}

proc convert_scope_to_plain_str {scope} {
    switch ${scope}\
          "all" {
                return [get_indep String All]
            }\
          "cl" {
                return [get_indep String ClassesNoKey]
            }\
          "com" {
                return [get_indep String CommonsNoKey]
            }\
          "con" {
                return [get_indep String ConstantsNoKey]
            }\
          "cov" {
                return [get_indep String CommonsVarsNoKey]
            }\
          "iu" {
                return [get_indep String IncludeNoKey]
            }\
          "e" {
                return [get_indep String Enums]
            }\
          "ec" {
                return [get_indep String EnumConsNoKey]
            }\
          "f" {
                return [get_indep String Files]
            }\
          "fr" {
                return [get_indep String FriendsNoKey]
            }\
          "fu" {
                return [get_indep String FunctionsNoKey]
            }\
          "fd" {
                return [get_indep String FunctionsDecNoKey]
            }\
          "gv" {
                return [get_indep String VariablesNoKey]
            }\
          "iv" {
                return [get_indep String ClassVarsNoKey]
            }\
          "iu" {
                return [get_indep String IncludeNoKey]
            }\
          "lv" {
                return [get_indep String PafLocVars]
            }\
          "ma" {
                return [get_indep String DefinesNoKey]
            }\
          "md" {
                return [get_indep String MethodsNoKey]
            }\
          "mi" {
                return [get_indep String MethodImpsNoKey]
            }\
          "su" {
                return [get_indep String SubroutinesNoKey]
            }\
          "t" {
                return [get_indep String Typedefs]
            }\
          "ud" {
                return [get_indep String Undefined]
            }\
          "un" {
                return [get_indep String UnionsNoKey]
            }
    return ""
}


proc convert_scope_to_sym {scope} {
    switch ${scope}\
          [get_indep String Classes] {
                return "cl"
            }\
          [get_indep String Functions] {
                return "fu"
            }\
          [get_indep String FunctionsDec] {
                return "fd"
            }\
          [get_indep String Commons] {
                return "com"
            }\
          [get_indep String CommonsVars] {
                return "cov"
            }\
          [get_indep String Methods] {
                return "md"
            }\
          [get_indep String MethodImps] {
                return "mi"
            }\
          [get_indep String ClassVars] {
                return "iv"
            }\
          [get_indep String Typedefs] {
                return "t"
            }\
          [get_indep String Variables] {
                return "gv"
            }\
          [get_indep String Enums] {
                return "e"
            }\
          [get_indep String EnumCons] {
                return "ec"
            }\
          [get_indep String Defines] {
                return "ma"
            }\
          [get_indep String Subr] {
                return "su"
            }\
          [get_indep String Const] {
                return "con"
            }\
          [get_indep String Unions] {
                return "un"
            }\
          [get_indep String PafLocVars] {
                return "lv"
            }\
          [get_indep String Friends] {
                return "fr"
            }\
          [get_indep String Includes] {
                return "iu"
            }\
          [get_indep String Files] {
                return "f"
            }\
          [get_indep String Undefined] {
                return "ud"
            }\
          [get_indep String All] {
                return "all"
            }

    return ""
}

# Update our display
proc sn_get_windows {{pat ""}} {
    set brws [lmatch [winfo children "."] ".sn-${pat}*"]
}


#This function is called after updating project
#like file save, reparse, ...
proc SN_Refresh_Windows {} {
    global sn_statistic_run
    global SN_Refresh_Windows_active

    if {! [info exists SN_Refresh_Windows_active]} {
        set SN_Refresh_Windows_active 0
    }
    if {${SN_Refresh_Windows_active}} {
        return
    }
    if {[info commands paf_db_f] == ""} {
        return
    }
    incr SN_Refresh_Windows_active 1

    #update the scope list
    sn_update_scope_list

    #refresh project editors
    Project&::Refresh_YourSelf

    #refresh statistic windows
    if {${sn_statistic_run}} {
        sn_update_statistic
    }

    #refresh the availiable Multi Views
    MultiWindow&::Refresh_YourSelf

    incr SN_Refresh_Windows_active -1
}

#This function is called when XRef process (dbimp) is finished
proc SN_Refresh_XRef_Windows {} {
    global SN_Refresh_XRef_Windows_active
    if {[info exists SN_Refresh_XRef_Windows_active] &&\
      ${SN_Refresh_XRef_Windows_active}} {
        return
    }
    set SN_Refresh_XRef_Windows_active 1

    #refresh the availiable Multi Views
    MultiWindow&::Refresh_YourSelf_After_XRef

    set SN_Refresh_XRef_Windows_active 0
}

proc sn_hide_show_project {cmd {mainw ""}} {
    global sn_options
    global sn_HideShowParams sn_debug
    global iconize_old_close_command

    if {${sn_debug}} {
        sn_log "HideShow: ${cmd}"
        catch {sn_log "  called from [info level -1]"}
    }

    #if toplevel is not specified, use any window
    if {${mainw} == ""} {
        set mainw [lindex [itcl::find objects "*" -class SymBr&] 0]
    }
    if {${mainw} == ""} {
        set mainw [lindex [itcl::find objects "*" -class MultiWindow&] 0]
    }

    if {${cmd} == "withdraw"} {
        catch {unset sn_HideShowParams}
        set foc [focus]

        if {${foc} != ""} {
            set active [winfo toplevel ${foc}]
        } else {
            set active ${mainw}
        }
        foreach win [winfo children .] {
            if {${win} == ".loading" || ${win} == ".sn_pop_menu_listbox"} {
                continue
            }

            if {[catch {set win [winfo toplevel ${win}]}]} {
                continue
            }
            set geo [wm geometry ${win}]
           set act [ expr {(${win} == ${active}) ? 1 : 0} ]

            set sn_HideShowParams(${win},EXISTS) 1
            set sn_HideShowParams(${win},WIN) ${win}
            set sn_HideShowParams(${win},GEOM) ${geo}
            set sn_HideShowParams(${win},STATE) [wm state ${win}]
            set sn_HideShowParams(${win},ACTIVE) ${act}

            if {${mainw} == ${win}} {
                #icon window

               #save icon name and replace it with project name
               set sn_HideShowParams(${win},ICONNAME) [wm iconname ${win}]
               wm iconname ${win} $sn_options(sys,project-name)

                wm iconify ${win}

                #save close command and replace it to close the project
                set iconize_old_close_command [wm protocol ${mainw}\
                  WM_DELETE_WINDOW]
                wm protocol ${mainw} WM_DELETE_WINDOW "MultiWindow& ::\
                  file_close_project"
            } else {
                if {$sn_options(def,wm-deiconify-withdraw)} {
                    wm deiconify ${win}
                }
                #hide window
                wm withdraw ${win}
            }
        }
        #use the same window to deiconify
        bind ${mainw} <Map> "sn_hide_show_project deiconify ${mainw}"
    }\
    elseif {${cmd} == "deiconify"} {
        bind ${mainw} <Map> " "
        if {[info exists iconize_old_close_command]} {
            wm protocol ${mainw} WM_DELETE_WINDOW ${iconize_old_close_command}
        }
        set act_geom ""
        foreach c [array names sn_HideShowParams "*,EXISTS"] {
            set p [lindex [split ${c} ","] 0]
            set win $sn_HideShowParams(${p},WIN)
            set geo $sn_HideShowParams(${p},GEOM)
            set sta $sn_HideShowParams(${p},STATE)
            set act $sn_HideShowParams(${p},ACTIVE)
           if {${mainw} == ${win}} {
               # icon window - restore icon name
               wm iconname ${win} $sn_HideShowParams(${p},ICONNAME)
           }

            catch {
                if {${act} == 0} {
                    if {$sn_options(def,wm_geometry) && [string range ${geo} 0\
                      2] != "1x1"} {
                        if {[itcl::find objects -isa sourcenav::Window ${win}] != ""} {
                            ${win} configure -geometry ${geo}
                        } else {
                            wm geometry ${win} ${geo}
                        }
                    }
                    switch ${sta} {
                        "normal" {
                                wm deiconify ${win}
                            }
                        "iconic" {
                                wm iconify ${win}
                            }
                    }
                } else {
                    set act_geom ${geo}
                    set active ${win}
                }
            }

        }
        catch {unset sn_HideShowParams}

        if {$sn_options(def,wm_geometry)} {
            if {[itcl::find objects -isa sourcenav::Window ${active}] != ""} {
                ${active} configure -geometry ${act_geom}
            } else {
                wm geom ${active} ${act_geom}
            }
        }
        catch {wm deiconify ${active}}
    }
}

proc wait_xref_end {procfd varb} {
    set end [gets ${procfd} line]
    if {${end} < 0} {
        #give the info that the process has been terminated
        upvar #0 ${varb} var
        set var "end"
        catch {close ${procfd}}

        #delete termometers from views
        xref_delete_termometers
    } else {
        #display xref info

        set scanning "Status: Scanning: "
        if {[string first $scanning ${line}] == 0} {
            set file [string range $line [string length $scanning] end]
            xref_termometer_disp $file 0
        }

        set deleting "Status: Deleting: "
        if {[string first $deleting ${line}] == 0} {
            set file [string range $line [string length $deleting] end]
            xref_termometer_disp $file 1
        }

        sn_log "wait_xref_end: ${line}"
    }
    update idletasks
}

# This function returns the location of a symbol checking duplications too.
proc sn_db_get_symbol {scope sym {qry_exp ""} {off_var ""}} {
    global sn_options sn_debug sn_sep

    if {${off_var} != ""} {
        upvar ${off_var} off
        set off 0
    } else {
        set off 0
    }
    sn_log "Query: scope: ${scope}, symbol: ${sym}, query: ${qry_exp}"

    #command not availiable
    if {[info commands paf_db_${scope}] == ""} {
        return ""
    }

    if {${qry_exp} != ""} {
        set lst [paf_db_${scope} seq -col ${qry_exp} "${sym}${sn_sep}"]
    } else {
        set lst [paf_db_${scope} seq "${sym}${sn_sep}"]
    }

    # Check whether we are searching for a class with a non existing template.
    if {${scope} == "cl" && ${lst} == ""} {
        set broff [string first {<} ${sym}]
        if {${broff} > -1} {
            set sym [string range ${sym} 0 [expr ${broff} - 1]]

            sn_log "Query2: scope: ${scope}, symbol: ${sym}, query: ${qry_exp}"

            if {${qry_exp} != ""} {
                set lst [paf_db_${scope} seq -col ${qry_exp} "${sym}${sn_sep}"]
            } else {
                set lst [paf_db_${scope} seq "${sym}${sn_sep}"]
            }
        }
    }

    sn_log "Query result <${lst}>"

    set lst [join [lindex ${lst} ${off}]]

    set begpos_off [lsearch -regexp ${lst} {[0-9]+\.[0-9]+}]
    set beg_pos [lindex ${lst} ${begpos_off}]
    set file [lindex ${lst} [expr ${begpos_off} + 1]]
    set end_pos [lindex ${lst} [expr ${begpos_off} + 2]]

    return [list ${file} ${beg_pos} ${end_pos} ${sym}]
}

#       w:      If not empty the name of the text widget containing the symbol
#               to search for.
#       scope:  a known scope such as: gv, mi, cl etc.
#       qry:    a expression (it can be empty too) such as -regexp {.*ab.*}
proc sn_get_detailed_for_selection {scope {name ""} {qry ""}} {
    set sym [sn_get_symbol_and_scope ${name}]
    if {${sym} != ""} {
        set name [lindex ${sym} 0]
        set scope [lindex ${sym} 1]
    }
    set pos [sn_db_get_symbol ${scope} ${name} ${qry} off]
    set name [lindex ${pos} end]
    set file [lindex ${pos} 0]
    set line [lindex ${pos} 1]
    set pos [lrange ${pos} 0 2]
    if {${file} == "" || ${line} == ""} {
        return ""
    }
    return [list ${scope} ${name} ${off} ${pos}]
}

#       w:      If not empty the name of the text widget containing the symbol
#               to search for.
#       scope:  a known scope such as: gv, mi, cl etc.
#       qry:    a expression (it can be empty too) such as -regexp {.*ab.*}
proc sn_display_object {scope {name ""} {qry ""}} {
    if {${scope} == "f"} {
        if {${name} == ""} {
            return
        }
        set exch_name [split ${name} "\t"]
        if {[llength ${exch_name}] > 1} {
            set name [file join [lindex ${exch_name} 1] [lindex ${exch_name} 0]]
        } else {
            set name [lindex ${exch_name} 0]
        }
        sn_edit_file dummy ${name}
        #sn_add_history "f" $name [sn_make_history_title edit f $name]
        return
    }
    set pars [sn_get_detailed_for_selection ${scope} ${name} ${qry}]
    if {${pars} == ""} {
        bell
        return
    }

    set scope [lindex ${pars} 0]
    set name [lindex ${pars} 1]
    set off [lindex ${pars} 2]
    set pos [lindex ${pars} 3]

    eval sn_edit_file [list "${off} ${name} ${scope}"] ${pos}

    #sn_add_history $scope [list $scope $name $off]\
      [sn_make_history_title edit $scope $name]
}

##
## Starts SN application.
## It's the script called from "etc/navigato"
proc sn_session {} {
    global sn_options
    global argv errorInfo sn_debug HOME tcl_platform

    if {[catch {wm withdraw .}]} {
        return
    }
    set appname [tk appname]

    #register by IDE
    maybe_ide_property set project-root $sn_options(sys,project-dir) project

    #this is the correct place to show the splash screen.
    #show the splash screen, only when no batch_mode
    # Also allow a nosplash arg. 
    if {(![sn_batch_mode]) && (![sn_nosplash])} {
        sn_show_splash_screen
    }

    #start project manager or directly open a project.
    sn_start_new_session
}



# This function is even called when a new interpreter is created
# to open a project or to create a new project.
proc sn_start_new_session {{opts ""}} {
    global sn_options
    global sn_arguments
    global tkeWinNumber
    global env sn_debug sn_path tcl_platform sn_statistic_run
    global argv argc

    sn_log "sn_start_new_session (${opts}, argc=${argc}, argv=${argv})"

    if {$tcl_platform(platform) == "windows"} {
        update
    }

    # Read command line arguments, if this is simulated by
    # calling this procedure from a new interpreter.
    if {${opts} != ""} {
        set argv ${opts}
        set argc [llength ${argv}]
        catch {unset sn_arguments}
        sn_read_commandline_arguments
    }

    maybe_ide_property set project-root $sn_options(sys,project-dir) project

    sn_log "calling sn_process_gui"
    # Open project, if a project is given on the command line
    set ret [sn_process_gui]

    sn_log "calling hide_loading_message"
    # Make sure that the loading message is put away.
    hide_loading_message

    if {!${ret}} {
        sn_log "deleteing interp in sn_start_new_session"

        # Note that once we delete this interp, we can not invoke
	# any commands or an error will be raised
        delete_interp
    }
}

proc sn_batch_mode {} {
    global sn_arguments
    if {[info exists sn_arguments(batchmode)] && $sn_arguments(batchmode)} {
        return 1
    }
    return 0
}

proc sn_nosplash {} {
    global sn_arguments
    if {[info exists sn_arguments(nosplash)] && $sn_arguments(nosplash)} {
        return 1
    }
    return 0
}

#opens a project or displays the project list to select a project.
proc sn_process_gui {} {
    global sn_options
    global sn_arguments
    global ProcessingCancelled

    sn_log "sn_process_gui: [array names sn_arguments]"

    #create a new project, imports filenames from a file
    if {[info exists sn_arguments(new)] && $sn_arguments(new)} {
        set ret [sn_new_project_cb]
        #don't exit when user cancels project creation,
        #just popup the project selector (when batchmode is disabled)
        if {! ${ret} && [sn_batch_mode]} {
            return ${ret}
        }
    }\
    elseif {[info exists sn_arguments(projectfile)]} {
        #open a project
        switch -- [sn_read_project $sn_arguments(projectfile)] {
            "0" {
                    #Error.
                    db_close_files

                    #don't exit!
                    set ProcessingCancelled 1
                }
            "-1" {
                    # I'm already using the project!
                }
            default {
                    #project open success
                    if {[sn_batch_mode]} {
                        #exit
                        return 0
                    } else {
                        #proceed
                        return 1
                    }
                }
        }
    }

    if {${ProcessingCancelled}} {
        return 0
    }

    #batchmode? Exit
    if {[sn_batch_mode]} {
        sn_log -stderr "batch mode can be only enabled by creating new projects"
        return 0
    }

    #ok, no open/create project, we popup the project list dialog.
    #select a project, only when no other projects are started
    if {[info commands paf_db_proj] == "" && [number_interp] == 1} {
        return [sn_select_project]
    }

    if {${ProcessingCancelled}} {
        return 0
    }

    return 1
}

proc sn_load_xref {xfer_file cbrowser_xref} {
    global sn_options
    global sn_path ProcessingCancelled
    global SN_cross_pid
    global xref_cancelled

    if {${ProcessingCancelled} || ${xfer_file} == "" || ![file exists\
      ${xfer_file}]} {
        return
    }

    if {[file size ${xfer_file}] == 0} {
        file delete -- ${xfer_file}
        return
    }

    catch {paf_db_to close}
    catch {paf_db_by close}

    # Generate xref.
    #
    # The output of the cbrowser executable needs to
    # be processed again by cbrowser2 before piping
    # it to dbimp. If no C/C++ files have been
    # parsed then just pass the file name containing
    # xref output to dbimp.

    set cbr2_cmd [list [file join $sn_path(parserdir) cbrowser2]]
    
    if {[string first "-l" $sn_options(sys,parser_switches)] != -1} {
        lappend cbr2_cmd -l
    }

    if {[info exists sn_options(macrofiles)] && $sn_options(macrofiles) != ""} {
        foreach mf $sn_options(macrofiles) {
            lappend cbr2_cmd -m ${mf}
        }
    }

    lappend cbr2_cmd -n $sn_options(db_files_prefix)
    lappend cbr2_cmd -c $sn_options(def,db_cachesize) \
        -C $sn_options(def,xref-db-cachesize)
    lappend cbr2_cmd ${xfer_file}

    set dbimp_cmd [list [file join $sn_path(parserdir) dbimp] \
        -H [info hostname] \
        -P [pid] -c $sn_options(def,db_cachesize) \
        -C $sn_options(def,xref-db-cachesize)]

    if {$cbrowser_xref} {
        lappend dbimp_cmd $sn_options(db_files_prefix)
        set cmd [concat $cbr2_cmd | $dbimp_cmd]
    } else {
        lappend dbimp_cmd -f ${xfer_file}
        lappend dbimp_cmd $sn_options(db_files_prefix)
        set cmd $dbimp_cmd
    }

    sn_log "cross-ref command: ${cmd}"

    if {[catch {set fd [open "| ${cmd}" r]} err]} {
        sn_error_dialog ${err}
        return
    }

    set SN_cross_pid ${fd}
    set xref_cancelled 0
    fconfigure ${fd} \
        -encoding utf-8 \
        -blocking 0
    fileevent ${fd} readable [list load_xref_pipe ${fd} ${xfer_file}]
}

#this procedure is called, when xref process finished 
proc load_xref_pipe {xreffd xfer_file} {
    global sn_options
    global SN_cross_pid sn_debug
    global xref_cancelled
    global xref_termometer

    #xref terminated
    if {${xref_cancelled} || [eof ${xreffd}]} {
        sn_log "cross-ref file \"${xfer_file}\" has been loaded"

        # It has to be called before sn_db_open_files!
        catch {unset SN_cross_pid}

# FIXME : Tcl fails to raise an error
# during a call to the close command
# when the program on the other end
# of a non-blocking pipe crashes.
# We work around this by putting the
# pipe back in blocking mode.
        fconfigure ${xreffd} -blocking 1

        set err ""
        set status [catch {close ${xreffd}} err]
        sn_log "xref pipe close-exit status: ${status}, ${err}"

        if {$status &&
                ([string match "*child killed*" $err] ||
                 [string match "*abnormal program termination*" $err] ||
                 [string match "*child process exited abnormally*" $err])} {
            set crashed 1
        } else {
            set crashed 0
        }

        # If dbimp or a second stage parser crashed, then
        # display an error. If a warning was generated
        # on stderr, just log it and continue. Don't
        # report an error if the user canceled.

        if {(($status && $sn_debug) || ${crashed}) && !${xref_cancelled}} {
            set errstring "Error: ${err}"
            if {[info exists xref_termometer(lastfile)] &&\
              $xref_termometer(lastfile) != ""} {
                set filestring "During xref-generation for:\
                  $xref_termometer(lastfile)\n"
            } else {
                set filestring ""
            }
            sn_error_dialog "${errstring}\n${filestring}\n[get_indep String\
              XRefHasBeenCrashed]"
        }

        #make sure we stay in project directory (different interpeters).
        catch {cd $sn_options(sys,project-dir)}

        #delete the lock file, so that the project isn't
        #reparsed by next load
        if {${status} || ${xref_cancelled}} {
            catch {[file delete -- $sn_options(db_files_prefix).lck]}
        }

        if {!${sn_debug}} {
            file delete -- ${xfer_file}
        }

        db_close_files 0

        #if xref has been cancelled, delete the related files
        if {${xref_cancelled}} {
            catch {file delete -- $sn_options(db_files_prefix).to}
            catch {file delete -- $sn_options(db_files_prefix).by}
            catch {file delete -- $sn_options(db_files_prefix).xfi}
        }

        #don't exist if xref db crashed, tell only the message
        set ret [sn_db_open_files 1 0]

        #delete termometers from views
        xref_delete_termometers

        #refresh windows after cross-ref end
        SN_Refresh_XRef_Windows

        if {${ret} != 1} {
            update idletasks
            return
        }

        if {$sn_options(def,xref-bell)} {
            bell
        }
    } else {
        set line [gets ${xreffd}]

        sn_log "Cross-ref PIPE: ${line}"

        # actualize termometer only in normal mode; do not show it in batch mode
        set scanning "Status: Scanning: "
        if {[string first $scanning ${line}] == 0} {
            set file [string range $line [string length $scanning] end]
            if {[sn_batch_mode]} { 
    	    } else { 
               xref_termometer_disp $file 0
            }
        }

        set deleting "Status: Deleting: "
        if {[string first $deleting ${line}] == 0} {
            set file [string range $line [string length $deleting] end]
            if {[sn_batch_mode]} { 
            } else { 
               xref_termometer_disp $file 1
            }
        }

    }

    update idletasks
}

#read the project list and resort it, so that a list
#of existing projects is built
proc sn_update_project_hotlist {} {
    global sn_options
    global sn_projects_list

    set exist_projs [sn_read_exist_projects]
    set proj_name $sn_options(sys,project-file)

    set off [lsearch -exact ${exist_projs} ${proj_name}]

    switch -exact -- ${off} {
        0 {
                return
            }
        -1 {
                set exist_projs [linsert ${exist_projs} 0 ${proj_name}]
            }
        default {
                set exist_projs [lreplace ${exist_projs} ${off} ${off}]
                set exist_projs [linsert ${exist_projs} 0 ${proj_name}]
            }
    }

    # Write it back!
    set pf $sn_projects_list(filename)

    if {[catch {set hlstfd [open ${pf} w+]} err]} {
        sn_error_dialog ${err}
        return 0
    } else {
        fconfigure ${hlstfd} \
            -encoding $sn_options(def,system-encoding) \
            -blocking 0
        puts ${hlstfd} [join ${exist_projs} "\n"]
        close ${hlstfd}
    }
}

proc sn_exit {} {
    if {[sn_quit]} {
        catch {destroy .}
        exit 0
    }
}

proc sn_quit {{exit ""}} {
    global sn_options
    global sn_emacs_socket

    #no project is availiable (while creating a new project)
    #so don't try to save the project
    if {[info commands paf_db_proj] == ""} {
        return 1
    }

    #verify if xref is running in the background
    if {${exit} == "" && [sn_processes_running]} {
        set answer [tk_dialog auto [sn_title [get_indep String Exit]]\
          "[get_indep String XRefIsRunning], [get_indep String ProjExit]"\
          question_image 0 [get_indep String Yes] [get_indep String No]]
        if {${answer} != 0} {
            return 0
        }
    }

    #verify if all windows can be closed.
    if {! [MultiWindow&::CloseAll]} {
        return 0
    }

    catch {close ${sn_emacs_socket}}
    sn_save_project
    sn_stop_process

    # Cancel the timer events!
    set ids [after info]
    if {${ids} != ""} {
        foreach id ${ids} {
            after cancel ${id}
        }
    }

    # Delete all opened windows
    foreach obj [itcl::find objects] {
        if {[winfo exists ${obj}]} {
            itcl::delete object ${obj}
        }
    }

    # Close the database after deleting all
    # the object otherwise objects can't save
    # settings to the db.  (Make class for one).
    db_close_files

    return 1
}

proc sn_copy_with_relative_paths {rel_dir files} {
    set dirs [list]
    set proj_len [string length ${rel_dir}]
    foreach d ${files} {
        set d [string trim ${d}]
        if {${rel_dir} == [string range ${d} 0 [expr ${proj_len} - 1]]} {
            lappend dirs [string range ${d} [expr ${proj_len} + 1] end]
        } else {
            lappend dirs ${d}
        }
    }
    return ${dirs}
}

proc sn_build_project_filename {dir {name ""}} {
    if {${name} == ""} {
        set name [file tail ${dir}]
    }
    if {[file extension ${name}] == ".proj"} {
        set name ${name}
    } else {
        set name "${name}.proj"
    }
    return [file join ${dir} ${name}]
}

proc generate_pathes {files} {
    set pdirs ""
    set od ""
    foreach f ${files} {
        set d [file dirname ${f}]
        if {${d} != ${od}} {
            lappend pdirs ${d}
            set od ${d}
        }
    }
    return [lunique [lsort ${pdirs}]]
}

proc choose_project_dir_cb {cls dir} {
    ${cls} configure -value ${dir}
}

proc choose_project_dir {cls} {
    Editor&::DirDialog ${cls} -script "choose_project_dir_cb ${cls}"\
      -prefix choose_project_dir
}

#Add a new Entry for a directory
proc add_more_cb {dirfr} {
    global tkeWinNumber
    global sn_newargs

    incr tkeWinNumber
    set lblwidth 20
    set newdir ${dirfr}.dir-${tkeWinNumber}
    set sn_newargs(add,$tkeWinNumber) ""

    set obj ${newdir}
    if {[itcl::find object $obj] == $obj} {
        itcl::delete object $obj
    }
    LabelEntryButton& ${newdir} -text [get_indep String AddDirectory]\
      -labelwidth ${lblwidth} -directory 1 -anchor nw -width 40 \
      -variable sn_newargs(add,$tkeWinNumber) -native 1\
      -buttonballoon [get_indep String ChooseINFO]\
      -state $sn_newargs(have-import-file)

    pack ${newdir} -side top -anchor nw -fill x
}

proc add_checkbuttons_to_fastcreate_dialog {w} {
    global sn_options
    global tkeWinNumber
    global sn_newargs

    set dirfr ${w}.dirs
    set lblwidth 20

    #Checkbuttons for recursive scan and creating xref
    #when -import is enabled, no recursive flag is editable
    if {$sn_newargs(have-import-file) == "normal"} {
        set labels [list [get_indep String ScanRecursive] [get_indep String\
          GenerateXRef]]
        set balloons [list [get_indep String ScanRecursiveINFO]\
          [get_indep String GenerateXRefINFO]]
        set values [list {-1 1} {-x ""}]
        set variables [list sn_options(def,scan-recursive)\
          sn_options(both,xref-create)]
    } else {
        set labels [list [get_indep String GenerateXRef]]
        set balloons [list [get_indep String GenerateXRefINFO]]
        set values [list {-x ""}]
        set variables [list sn_options(both,xref-create)]
    }

    frame ${w}.dirs

    #Projectname with full path (save dialog)
    set obj ${dirfr}.prjname
    if {[itcl::find object $obj] == $obj} {
        itcl::delete object $obj
    }
    LabelEntryButton& $obj -text [get_indep String ProjectName]\
      -labelwidth ${lblwidth} -save_open "save"\
      -extensions $sn_options(project_extensions) -defaultextension ".proj"\
      -anchor nw -width 40 -variable sn_newargs(path) -native 1\
      -buttonballoon [get_indep String ChooseINFO]
    pack ${dirfr}.prjname -side top -anchor nw -fill x

    #no adding directories, when -import is specified.
    if {$sn_newargs(have-import-file) == "normal"} {
        add_more_cb ${dirfr}
    }

    #initialize the first directory with the base directory of the project file
    set sn_newargs(add,${tkeWinNumber}) [file nativename [file dirname\
      $sn_newargs(path)]]
    set sn_newargs(old-dir) $sn_newargs(add,${tkeWinNumber})

    #no "more" button, when -import is specified
    if {$sn_newargs(have-import-file) == "normal"} {
        frame ${w}.more
        button ${w}.more.more -text [get_indep String more] -command\
          " add_more_cb ${w}.dirs " -state $sn_newargs(have-import-file)
    }

    if {[itcl::find object ${w}.checkbtns] == "${w}.checkbtns"} {
        itcl::delete object ${w}.checkbtns
    }
    CheckButton& ${w}.checkbtns -labels ${labels} -balloons ${balloons}\
      -values ${values} -variables ${variables} -label ""

    pack ${w}.checkbtns -in ${w}.top -side bottom -fill x -padx 10m -pady 3m
    if {$sn_newargs(have-import-file) == "normal"} {
        pack ${w}.more.more -side top -anchor nw
        pack ${w}.more -in ${w}.top -fill x -side bottom -padx 10m -pady 3m
    }
    pack ${dirfr} -in ${w}.top -side bottom -fill x -padx 10m -pady 3m
}

proc sn_constructe_name {{path ""}} {
    global sn_options
    if {${path} == ""} {
        set path $sn_options(sys,project-file)
    }
    set name [file tail ${path}]
    if {[file extension ${name}] == ".proj"} {
        set name [file root ${name}]
    }
    if {${name} == ""} {
        set name "noname"
    }
    return ${name}
}

proc user_new_directories {} {
    global sn_options
    global sn_newargs
    set dirs ""
    foreach aname [array names sn_newargs "add,*"] {
        set dname $sn_newargs(${aname})
        if {${dname} == "" || ![file isdirectory ${dname}]} {
            #skip unusable directories
            continue
        }
        lappend dirs [realpath -pwd $sn_options(sys,project-dir) ${dname}]
    }
    return ${dirs}
}

#command could be "init", "close", "dir", "file", "message", ..
proc sn_glob_updatecommand {cmd {dir ""}} {
    global SN_glob_Cancel_Flag

    if {![info exists SN_glob_Cancel_Flag]} {
        set SN_glob_Cancel_Flag 0
    }

    if {${cmd} == "init"} {
        #init global variable
        #make sure that the window is created
        sn_loading_message
        set SN_glob_Cancel_Flag 0
        sn_loading_enable_cancel SN_glob_Cancel_Flag
        return "0"
    }\
    elseif {${cmd} == "close"} {
        #disable cancel button
        sn_loading_disable_cancel
        return "0"
    }

    #display current scanned directory
    sn_loading_message ${dir} [get_indep String Scanning]

    #return status to (continue/break) process
    if {[info exists SN_glob_Cancel_Flag]} {
        return ${SN_glob_Cancel_Flag}
    } else {
        return "0"
    }
}

proc sn_create_new_project {{import_file ""}} {
    global sn_options sn_arguments
    global tcl_platform
    global sn_newargs
    global ProcessingCancelled
    global Avail_Parsers Parser_Info
    global xref_termometer

    if {$tcl_platform(platform) == "windows"} {
        set sn_options(include-source-directories) "."
    } else {
        if {[file isdirectory /usr/include]} {
            set sn_options(include-source-directories) [list /usr/include .]
        }
    }

    # Concatenate the known file extensions!
    # to pass it to the glob command
    set glob_expr [list]
    foreach p ${Avail_Parsers} {
        foreach e $Parser_Info(${p},SUF) {
            if {[string index ${e} 0] != "*"} {
                set e "*${e}"
            }
            lappend glob_expr [list ${e}]
        }
    }
    set glob_expr [lunique [lsort [join ${glob_expr}]]]
    set glob_expr [join ${glob_expr}]

    #init some variables
    #initialize the project variables for the actual directory
    set sn_options(sys,project-dir) [pwd]
    set sn_options(sys,project-file)\
      [sn_build_project_filename $sn_options(sys,project-dir)]

    #now we have to overrite the above settings with the command
    #line parameters
    if {[info exists sn_arguments(projectfile)]} {
        set sn_arguments(projectfile) [realpath\
          -pwd $sn_options(sys,project-dir) $sn_arguments(projectfile)]
        if {$sn_arguments(projectfile) != "" && ! [file isdirectory\
          $sn_arguments(projectfile)]} {
            set sn_options(sys,project-dir) [file dirname\
              $sn_arguments(projectfile)]
            #make sure that ".proj" is an extension of the project name
            set sn_options(sys,project-file)\
              [sn_build_project_filename $sn_options(sys,project-dir)\
              $sn_arguments(projectfile)]
        } else {
            sn_error_dialog [format [get_indep String WrongProjectName]\
              $sn_arguments(projectfile)]
            return 0
        }
    }
    #user has specified the database directory in the command line
    if {[info exists sn_arguments(databasedir)]} {
        if {![file exists $sn_arguments(databasedir)]} {
            set ret [catch {file mkdir $sn_arguments(databasedir)}]
        } else {
            set ret 0
        }
        if {[file isdirectory $sn_arguments(databasedir)] || ${ret}} {
            set sn_options(both,db-directory) $sn_arguments(databasedir)
        } else {
            sn_error_dialog [format [get_indep String WrongDatabaseDirectory]\
              $sn_arguments(databasedir)]
            return 0
        }
    }

    #set the correct project name to a global variable
    set sn_options(sys,project-name)\
      [sn_constructe_name $sn_options(sys,project-file)]
    set sn_newargs(path) [file nativename $sn_options(sys,project-file)]

    #import entries from a file
    set fil_list [list]
    if {${import_file} != ""} {
        if {![file exists $import_file]} {
	    sn_error_dialog [format \
		    [get_indep String ErrorImportFileDoesNotExists] \
		    $import_file]
            sn_exit
	}

	if {[file isdirectory $import_file]} {
	    sn_error_dialog [format \
		   [get_indep String ErrorImportFileIsADirectory] $import_file]
	    sn_exit
	}

	set fd [open ${import_file}]
        fconfigure ${fd} \
            -encoding $sn_options(def,system-encoding) \
            -blocking 0
        set files [split [read -nonewline ${fd}] "\n"]
        close ${fd}

        foreach f [sn_copy_with_relative_paths $sn_options(sys,project-dir)\
          ${files}] {
            if {![file exists ${f}] || ![file readable ${f}]} {
                continue
            }

            if {[file isdirectory ${f}]} {
                #set contents [glob -nocomplain -- [file join $f "*"]]
                set contents [sn_glob -nocomplain -match ${glob_expr}\
                  -ignore $sn_options(def,ignored-directories) -dirlevel 0\
                  -dirvar ignore_dirs -updatecommand "sn_glob_updatecommand"\
                  -- ${f}]
                #add only regular files
                foreach subfile ${contents} {
                    if {! [file isfile ${subfile}]} {
                        continue
                    }
                    lappend fil_list ${subfile}
                }
            }\
            elseif {[file type ${f}] == "file"} {
                lappend fil_list ${f}
            }
        }
        set answer 1
        set sn_newargs(have-import-file) disabled
    } else {
        set sn_newargs(have-import-file) normal
    }

    sn_log -l 2 "Source files:\n[join ${fil_list} "\n"]"

    hide_loading_message

    #call fast-create window, when "-import" isn't specified
    #Generate file list for the new created project
    #First ask the user if he wants to fast-create the project
    #based on the actual directory
    #default scan recursive
    set sn_options(def,scan-recursive) -1
    if {! [sn_batch_mode]} {
        foreach obj [itcl::find objects .newprj-dlg.*] {
# FIXME : We need to turn this mess into an itk component!!!!
            itcl::delete object $obj
        }
        foreach aname [array names sn_newargs "add,*"] {
            catch {unset sn_newargs(${aname})}
        }
        set answer [tk_dialog_with_widgets .newprj-dlg [get_indep String\
          FastCreateProject] "[get_indep String FastCreateProjectQuestion]"\
          question_image 0 add_checkbuttons_to_fastcreate_dialog\
          [get_indep String Ok] [get_indep String ProjectEditor]\
          [get_indep String Cancel]]

        if {${answer} == 2} {
            set ProcessingCancelled 1
            return 0
        }
    } else {
        set sn_newargs(add,1) $sn_options(sys,project-dir)
        set answer 0
    }

    #build project name based on directory name
    set sn_options(sys,project-file) $sn_newargs(path)
    set sn_options(sys,project-name)\
      [sn_constructe_name $sn_options(sys,project-file)]
    set sn_options(sys,project-dir) [file dirname $sn_newargs(path)]

    #directory doesn't exist
    if {![file exists $sn_options(sys,project-dir)] || ![file isdirectory\
      $sn_options(sys,project-dir)]} {
        sn_error_dialog [format [get_indep String UnknownDir]\
          $sn_options(sys,project-dir)]
        set ProcessingCancelled 1
        return 0
    }

    # Bail out if an import directory does not exist
    foreach aname [array names sn_newargs "add,*"] {
        if {$sn_newargs(${aname}) == ""} {
            continue
        }
	# Bail out if import dir does not exist!
	if {![file isdirectory $sn_newargs(${aname})]} {		     
	    sn_error_dialog [format [get_indep String UnknownDir]\
	        $sn_newargs(${aname})]
	    set ProcessingCancelled 1
	    return 0
	}
    }

    #the files are already predefined
    if {$sn_newargs(have-import-file) == "disabled"} {
        if {${answer} == 0} {
            set answer 2
        } else {
            set answer 3
        }
    }

    switch -- ${answer} {
        "0" {
                #verify if the file exists or is being used
                if {! [Project&::can_create_project [file dirname\
                  $sn_newargs(path)] [file tail $sn_newargs(path)]]} {
                    return 0
                }

                #read the files from the selected directories
                sn_loading_message

                set i 0
                set ff ""
                foreach aname [array names sn_newargs "add,*"] {
                    if {$sn_newargs(${aname}) == ""} {
                        continue
                    }
                    set dir [realpath -pwd $sn_options(sys,project-dir)\
                      $sn_newargs(${aname})]
                    set ffiles [sn_glob -match ${glob_expr}\
                      -dirlevel $sn_options(def,scan-recursive)\
                      -ignore $sn_options(def,ignored-directories)\
                      -dirvar ignore_dirs\
                      -updatecommand "sn_glob_updatecommand" -- ${dir}]

                    eval lappend ff ${ffiles}
                    incr i
                }

                # If there is more than one directory specified,
                # make a unique list.
                if {${i} > 1} {
                    set ff [lunique [lsort ${ff}]]
                }
            }
        "2" {
                set ff [lunique [lsort ${fil_list}]]
            }
        "3" {
                #call project editor
                #use the new project editor to add new files to the project
                set t .project_editor
                if {[winfo exist ${t}]} {
                    catch {destroy ${t}}
                }
# FIXME: This variable set stuff is a major hack, we need a generic sort
# of window that supports waiting until the window is closed. This also
# needs to be cleaned up inside the Project& class.
                global wait_for_projecteditor
                # This next line is a hack because
                # wait_for_projecteditor might not be set.
                set wait_for_projecteditor ""
                Project& ${t} -new_project 1 -new_ProjectFiles ${fil_list}\
                  -variable wait_for_projecteditor
                if {${wait_for_projecteditor} != "ok"} {
                    set ProcessingCancelled 1
                    itcl::delete object ${t}
                    return 0
                }
                set ff [${t} contents]
            }
        default {
                #verify if the user has specified some directories,
                #if this is the case ask the user if he wants to
                #initialize the project editor with this directories.
                set dirs [user_new_directories]
                set dirname [lindex [array names sn_newargs "add,*"] end]

                if {[llength ${dirs}] > 1 || $sn_newargs(old-dir) !=\
                  $sn_newargs(${dirname})} {
                    set answer [tk_dialog .ask-info [get_indep String\
                      ProjectEditor] "[get_indep String\
                      YouHaveAddedSomeDirectories]:\n[join ${dirs}\
                      \n]\n[get_indep String DoYouWantToInitProjEditor]"\
                      question_image 0 [get_indep String Yes]\
                      [get_indep String No]]
                    if {${answer} == 0} {

                        sn_loading_message

                        set fil_list ""
                        set i 0
                        foreach dir ${dirs} {
                            set ffiles [sn_glob -match ${glob_expr}\
                              -dirlevel $sn_options(def,scan-recursive)\
                              -dirvar ignore_dirs\
                              -ignore $sn_options(def,ignored-directories)\
                              -updatecommand "sn_glob_updatecommand" -- ${dir}]
                            eval lappend fil_list ${ffiles}
                            incr i
                        }
                        if {${i} > 1} {
                            set fil_list [lunique [lsort ${fil_list}]]
                        }
                        hide_loading_message
                    }
                }

                #use the new project editor to add new files to the project
                set t .project_editor
                if {[winfo exist ${t}]} {
                    itcl::delete object ${t}
                }
# FIMXE: another example of the Project& wait hack
                global wait_for_projecteditor
		# hack, since wait_for_projecteditor might
		# not be set yet...
		#                set wait_for_projecteditor ""
                Project& ${t} -new_project 1 -new_ProjectFiles ${fil_list}\
		  -variable wait_for_projecteditor
		if {${wait_for_projecteditor} != "ok"} {
                    set ProcessingCancelled 1
                    itcl::delete object ${t}
                    return 0
                }
                set ff [${t} contents]
            }
    }

    #build project name based on directory name
    set sn_options(sys,project-name)\
      [sn_constructe_name $sn_options(sys,project-file)]
    set sn_options(sys,project-dir) [file dirname $sn_options(sys,project-file)]

    #now delete prefix for files in the project directory
    set fil_list ""
    set len [expr [string length $sn_options(sys,project-dir)] + 1]
    foreach f ${ff} {
        set f [sn_truncate_path $sn_options(sys,project-dir) ${f}]
        lappend fil_list ${f}
    }

    #delete the project editor window
    catch {destroy ${t}}

    sn_loading_message

    #change to choosen directory, if we could !!
    set ret [catch {cd $sn_options(sys,project-dir)} err]
    if {${ret}} {
        sn_error_dialog ${err}
        set ProcessingCancelled 1
        return 0
    }

    sn_log "create new project: project directory files..."

    #save settings into the proiect
    if {![sn_save_project 0 "new"]} {
        set ProcessingCancelled 1
        return 0
    }

    sn_loading_message [get_indep String Scanning]

    sn_log "create new project: project directory files..."

    #add all directory prefixes to the include path list and
    #store the directories of the project into a database (.icl)
    eval lappend sn_options(include-source-directories)\
      [generate_pathes ${fil_list}]
    set_project_dir_files ${fil_list}

    # Try to auto-detect the type of revision control they are using.
    # Loop through the list of version control systems we support looking for
    # a directory in the project-dir with the same name (like project/CVS)
    set matched_types [list]
    foreach pair $sn_options(sys,supported-vcsystems) {
        foreach {type desc} ${pair} break
        set search_dir [file join $sn_options(sys,project-dir) ${type}]
        if {[file isdirectory ${search_dir}]} {
            lappend matched_types [list ${type} ${desc}]
        }
    }

    # We only switch from the default version control system if we find a single
    # directory that matches the types from the search list, because if they
    # had both a CVS and a RCS directory we would not know which one to pick!

    if {[llength ${matched_types}] == 1} {
        set match [lindex [lindex ${matched_types} 0] 1]
        sn_log "auto switch to ${match} revision control system"
        set sn_options(both,rcs-type) ${match}
    }

    # Now we have to figure out which groups (files) have parsers,
    # and how many files will be parsed.
    set filenum 0
    set files_without_parser ""
    foreach file ${fil_list} {
        set type [sn_get_file_type ${file}]
        if {$Parser_Info(${type},BROW) == "" || $Parser_Info(${type},TYPE) ==\
          "others"} {
            lappend files_without_parser ${file}
        } else {
            lappend parserfiles(${type}) ${file}
            incr filenum
        }
    }

    # Make sure that the temporary files are created in the
    # symbol db directory! this because on some OS's the
    # temporary directory doesn't have alot of space
    sn_set_tmp_dir $sn_options(both,db-directory)

    #to store xref-info for dbimp
    set xfer_file [sn_tmpFileName]

    #create temp file so that sn_tmpFileName doesn't use
    # the same filename twice (itr - 12.12.97)
    set tmp_fd [open ${xfer_file} w]
    close ${tmp_fd}

    hide_loading_message

    #create a window with the scale widget to view the position
    #of parsing
    set scale_window [make_scale_window ${filenum} 1]

    #Now we can start parsers on the source code files
    #reset the project file list, it could be the case that
    #the user breaks parsing where not all files have been
    #added to the project
    set fil_list ""
    set file_types ""
    set cbrowser_xref 0
    foreach type [array names parserfiles] {
        set files [lsort -dictionary $parserfiles(${type})]

        set brow_exec $Parser_Info(${type},BROW)
        set brow_swi $Parser_Info(${type},BROW_SWITCH)

        lappend file_types ${type}

        eval lappend fil_list ${files}

        set brow_cmd [list ${brow_exec}]
        if {${brow_exec} != "" && ${files} != ""} {
            # If parsing c/c++ file with cbrowser, pass
            # a flag to sn_load_xref so that it knows to
            # use cbrowser2 in the xref gen stage.
            if {[string first cbrowser $brow_exec] != -1} {
                set cbrowser_xref 1
            }

            #append macro files to the parser
            set macroflag $Parser_Info(${type},MACRO)
            if {${macroflag} == "-m"} {
                foreach mf $sn_options(macrofiles) {
                    lappend brow_cmd -m ${mf}
                }
            }
            if {${brow_swi} != ""} {
                eval lappend brow_cmd ${brow_swi}
            }
            #'-t' means drop /... files.
            lappend brow_cmd -t

            #we must verify the exactly number of files to generate
            #xref-information for.
            if {[lsearch -exact $sn_options(sys,language-with-xref) ${type}]\
              != -1} {
                xref_termometer_files + ${files}
            }

            #now run the parser and add the files into the project
            if {![sn_load_files ${brow_cmd} ${files} ${xfer_file}\
              ${scale_window}] || ${ProcessingCancelled}} {
                # If there was a problem parsing the files
                # we give up trying to create this project.
                if {![sn_batch_mode]} {
                    delete_scale_window ${scale_window}
                }
                return 0
            }
        }
    }

    if {${files_without_parser} != ""} {
        load_files_without_parser ${files_without_parser}
        eval lappend fil_list ${files_without_parser}
    }

    #sort source files
    set fil_list [lsort -dictionary ${fil_list}]

    # It will be used only if the working directory
    # of the project will be sometime deleted.
    paf_db_proj put include-source-directories\
      $sn_options(include-source-directories)
    paf_db_proj put source_files ${fil_list}

    # Possibly notify the IDE of our file list.
    maybe_ide_property set project-files ${fil_list} project

    paf_db_proj sync
    
    if {![sn_batch_mode]} {
	delete_scale_window ${scale_window}
    }

    update idletasks
    update

    # Generate XRef information:
    #
    # Note that we delay starting the xref process in the
    # pipe for a moment so that the symbol browser can
    # be mapped. This avoids a problem under Windown 95/98
    # where there is a long pause between the time the
    # file scanning is done and the symbol browser shows up.
    #
    # This delay causes the batch mode to stop prematurely,
    # without creating the xref tables. (reason?)
    # Therefore, I removed it again (Bart Van Rompaey - 
    # bart.vanrompaey2@ua.ac.be)
    
    # quick fix: dont build xref when cmdline says so
    if {$sn_options(def,xref-skip)} {
    	set xfer_file ""
    }
    
    if {${xfer_file} != "" && [file exists ${xfer_file}] && [file size\
      ${xfer_file}] > 0} {
        #after 1000 [list sn_load_xref ${xfer_file} ${cbrowser_xref}]
        sn_load_xref ${xfer_file} ${cbrowser_xref}
    }

    catch {paf_db_proj sync}

    #by interactive mode, don't open the project, just exit
    if {[sn_batch_mode]} {
        if {[sn_processes_running]} {
            global sn_wait_xref_flag

            puts stdout [get_indep String WaitingForXref]
            flush stdout

            set sn_wait_xref_flag "waiting"

            sn_is_waiting_for_xref

            tkwait variable sn_wait_xref_flag
        }

        puts stdout [get_indep String ProjectBatchBuilt]
        flush stdout

        #exit program
        set ProcessingCancelled 0
        return 0
    }

    sn_log "verify database files..."
    if {[catch {set ret [sn_db_open_files 0 0]}] || ${ret} < 0} {
        sn_log "error by opening db files return status <${ret}>"
        set ProcessingCancelled 0
        return 0
    }

    sn_finalize_project new

    sn_log "create new project: project created."

    return 1
}

######################################################################
## This function initialize the database for the include directories
## that will be used to localize included files with the preprocessor
## routines like '#include <stdio>'
##
## Since we use a sn_options(source_files) variable to store the
## included directories is this file useless for SN.
######################################################################
proc set_project_dir_files {{files ""}} {
    global sn_options
    global sn_debug

    if {${files} == ""} {
        set files [sn_project_file_list]
    }
    if {${files} == ""} {
        return
    }
    set db_pref $sn_options(db_files_prefix)

    set file_list [sn_tmpFileName]
    if {[catch {set fd [open ${file_list} "w+"]}]} {
        return
    }
    fconfigure ${fd} \
        -encoding $sn_options(def,system-encoding) \
        -blocking 0
    puts ${fd} [join ${files} \n]
    close ${fd}

    sn_log "save project directories: ${db_pref}.icl,\
      $sn_options(def,db_cachesize), from: ${file_list}."

    if {[catch {sn_put_project_dir_files\
      "${db_pref}.icl" $sn_options(def,db_cachesize) ${file_list}}]} {
        sn_error_dialog [get_indep String CannotSaveIncludeDirectories]
    }

    if {!${sn_debug}} {
        catch {file delete -- ${file_list}}
    }
}

proc sn_browser_scope_menu_labels {} {
    global sn_browser_scope_menu_labels

    if {[info exists sn_browser_scope_menu_labels]} {
        return ${sn_browser_scope_menu_labels}
    }

    set options [list [get_indep String Files] [get_indep String Classes]\
      [get_indep String Methods] [get_indep String ClassVars]\
      [get_indep String Friends] [get_indep String Functions]\
      [get_indep String Typedefs] [get_indep String Variables]\
      [get_indep String Enums] [get_indep String EnumCons] [get_indep String\
      Unions] [get_indep String Defines] [get_indep String Const]]

    #add fortran scopes, when availiable
    foreach sc {su com cov} {
        if {[info commands paf_db_${sc}] != ""} {
            lappend options [get_indep String Subr] [get_indep String Commons]\
              [get_indep String CommonsVars]
            break

        }
    }

    set sn_browser_scope_menu_labels [lsort ${options}]

    return ${sn_browser_scope_menu_labels}
}

proc sn_init_rcs_variables {} {
    global sn_options

    set sn_options(both,rcs-type) [string tolower $sn_options(both,rcs-type)]

    # Choose the RCS Version:
    # Set the proper sn_rcs_xxx variables:
    # This works for ALL variables define above (because of the use of the\
      'info'
    # command). Clever ;-)
    set i [string length sn_rcs_$sn_options(both,rcs-type)]
    foreach item [info globals sn_rcs_$sn_options(both,rcs-type)*] {
        upvar #0 ${item} v
        set suf [string range ${item} ${i} end]
        uplevel #0 "set sn_rcs${suf} [list ${v}]"
    }
}

#given a view name, open the files assigned to this view
proc sn_db_open_exclude {{exclude ""}} {
    global sn_options
    global errorCode

    if {${exclude} == ""} {
        set exclude [paf_db_proj get -key db_exclude]
        if {${exclude} == ""} {
            set exclude "default"
        }
    }

    catch {paf_db_exclude close}

    set views [paf_db_proj get -key views]

    #see if the view exists
    set off -1
    set i 0
    foreach v ${views} {
        if {[lindex ${v} 0] == ${exclude}} {
            set off ${i}
            break

        }
        incr i
    }
    if {${off} == -1} {
        set proj_pref $sn_options(db_files_prefix)

        set len [expr [llength ${views}] + 1]
        set file ${proj_pref}.${len}
        lappend views [list ${exclude} ${file}]
        paf_db_proj put views ${views}

        sn_log "open_exclude view <${views}> pwd <[pwd]>"
    } else {
        set file [lindex [lindex ${views} ${off}] 1]
        sn_log "open_exclude file <${file}> pwd <[pwd]> ProjDir\
          <$sn_options(sys,project-dir)>"
    }

    set file [file join $sn_options(sys,project-dir) ${file}]
    sn_log "open_exclude file now <${file}>"

    #create database and reopen it in readonly mode.
    foreach of {{RDWR CREAT} RDONLY} {
        if {![catch {dbopen paf_db_exclude ${file} ${of} [sn_db_perms] btree}\
          err]} {
            set err ""
            break
        }
    }

    if {${err} != ""} {
        paf_db_proj sync
        sn_error_dialog ${err}

        return ""
    }
    paf_db_proj put db_exclude ${exclude}
    paf_db_proj sync

    return ${exclude}
}

proc sn_change_view {view} {

    sn_db_open_exclude ${view}

    db_close_files 0

    sn_db_open_files

    SN_Refresh_Windows
}

proc sn_processes_running {{show_message 0}} {
    global SN_cross_pid

    if {[info exists SN_cross_pid]} {
        if {${show_message}} {
            sn_error_dialog [get_indep String BackProcsRun]
        }
        return 1
    }
    return 0
}

#This function does three things
#1. Load new files into project
#2. Hide files from the given view
#3. Delete (unload) files from project.
proc sn_load_hide_unload_files {dir loadf {hidefiles ""} {unloadf ""} {view\
  "default"} {busy 1}} {
    global sn_options
    global sn_path sn_debug

    #make sure we stay in project directory (different interpeters).
    catch {cd $sn_options(sys,project-dir)}

    if {[sn_processes_running 1]} {
        return 0
    }

    #all project files
    set proj_files [sn_project_file_list 0]

    #1. hide files
    if {[string compare ${hidefiles} ""] != 0} {
        sn_db_open_exclude ${view}

        foreach f ${hidefiles} {
            if {[lsearch -exact ${proj_files} ${f}] != -1} {
                catch {paf_db_exclude put ${f} "#"}
            }
        }
    }

    #2. delete files from the project
    if {[string compare ${unloadf} ""] != 0} {
        set unloadfiles ${unloadf}
        set unloadf ""
        foreach f ${unloadfiles} {
            #We might delete hidden files that cannot be in '$proj_files'.
            lappend unloadf ${f}
            # It cannot be hidden !
            catch {paf_db_exclude del ${f}}
        }
    }

    if {[string compare ${unloadf} ""] != 0} {
        set fdd_filename [sn_make_delfilelist ${unloadf}]
    } else {
        set fdd_filename ""
    }

    #do we have cross-ref.
    if {[info commands paf_db_to] != ""} {
        set have_xref 1
    } else {
        set have_xref 0
    }

    #close database files
    db_close_files 0

    if {[string compare ${unloadf} ""] != 0 && ! [catch {dbopen del_file\
      $sn_options(db_files_prefix).f RDWR [sn_db_perms] btree}]} {
        sn_log "Removing ${unloadf}"

        set src_files [sn_project_file_list 0]
        foreach f ${unloadf} {
            del_file del ${f}
            set off [lsearch -exact ${src_files} ${f}]
            if {${off} >= 0} {
                set src_files [lreplace ${src_files} ${off} ${off}]
            }
        }
        del_file close

        paf_db_proj put source_files ${src_files}
        maybe_ide_property set project-files ${src_files} project

        set tmpf [sn_tmpFileName]
        set unloadfd [open ${tmpf} "w+"]
        fconfigure ${unloadfd} \
            -encoding utf-8 \
            -blocking 0
        foreach f ${unloadf} {
            puts ${unloadfd} "-1;$sn_options(db_del_type);${f}"
        }
        #		puts $unloadfd "-2;$sn_options(db_del_type);[join $unloadf ","]"
        sn_log "file symbols for unload are in: ${fdd_filename}"
        puts ${unloadfd} "-3;$sn_options(db_del_type);${fdd_filename}"

        close ${unloadfd}

        set dbexec [file join $sn_path(parserdir) dbimp]

        set cmd [list ${dbexec} -f ${tmpf} -c $sn_options(def,db_cachesize)\
          -C $sn_options(def,xref-db-cachesize) $sn_options(db_files_prefix)]

        #set termometer data
        if {${have_xref}} {
            xref_termometer_files + ${unloadf} 2
        } else {
            xref_termometer_files + ${unloadf}
        }

        #  X events must be processed !
        set unloadfd2 [open "| ${cmd}" r]
        fconfigure ${unloadfd2} \
            -encoding utf-8 \
            -blocking 0
        fileevent ${unloadfd2} readable [list wait_xref_end ${unloadfd2}\
          X_unload]
        vwait X_unload

        if {${sn_debug}} {
            sn_log "Unload files from \"${tmpf}\""
        } else {
            file delete -- ${tmpf}
        }
    }

    #reopen the database files
    sn_db_open_files

    #3. eventually load new files
    if {[string compare ${loadf} ""] != 0} {
        sn_parse_uptodate ${loadf}
    }

    #refresh project windows
    SN_Refresh_Windows

    return 1
}

# This function stops in the background running processes.
proc sn_stop_process {} {
    global sn_options
    global SN_cross_pid

    #make sure we stay in project directory (different interpeters).
    catch {cd $sn_options(sys,project-dir)}

    if {![sn_processes_running]} {
        return
    }

    if {[catch {set xref_cmd [fileevent ${SN_cross_pid} readable]}]} {
        bell
        return
    }
    fileevent ${SN_cross_pid} readable {}

# FIXME: We need to add a kill command to Tcl that will provide a cross platform
# way to stop a subprocess. This code only works under Unix.
    sn_log "killing SN_cross_pid \"[pid ${SN_cross_pid}]\""
    catch {exec kill [pid ${SN_cross_pid}]}
    catch {close ${SN_cross_pid}}
    catch {unset SN_cross_pid}

    catch {set xref_file [lindex ${xref_cmd} end]}
    file delete -force -- ${xref_file}

    if {[catch {set pref $sn_options(db_files_prefix)}]} {
        return
    }
    # Because we have killed the process the xref database files are
    # not complete, thus we have to delete them.
    set fls [glob -nocomplain ${pref}.to ${pref}.by ${pref}.ctr ${pref}.xfi]
    if {${fls} != ""} {
        eval file delete -force -- ${fls}
    }
}

proc sn_delete_current_project {{interactive 1}} {
    global sn_options
    global tcl_platform

    set pfile $sn_options(sys,project-file)
    if {$tcl_platform(platform) == "windows"} {
        regsub -all {/} ${pfile} {\\} pfile
    }
    if {${interactive}} {
        set answer [tk_dialog auto [get_indep String ProjectDelete]\
          "[get_indep String DeleteProjectQuestion] \"${pfile}\" ?"\
          question_image 0 [get_indep String Yes] [get_indep String No]]

        if {${answer} != 0} {
            return 0
        }
    }

    sn_log "delete current project:$sn_options(sys,project-file)"

    sn_stop_process
    sn_db_delete_files $sn_options(sys,project-dir) $sn_options(db_files_prefix)

    sn_log "delete project file: $sn_options(sys,project-file)"
    if {[catch {file delete -- $sn_options(sys,project-file)} err]} {
        sn_error_dialog ${err}
    }

    sn_log "delete current project: project deleted"

    return 1
}

# Check lock information in the database, check to see if
# the other process is active, and return the found info
# by setting variables in the caller's scope.

proc sn_is_project_busy {nm intp usr hst port p} {
    global sn_options
    global tcl_platform errorCode

    upvar ${intp} in
    upvar ${usr} user
    upvar ${hst} host
    upvar ${port} tcp_ip_port
    upvar ${p} pid

    set in ""
    set user ""
    set host ""
    set pid ""

    if {[catch {cd [file dirname ${nm}]}] || ![file exists ${nm}] ||\
      [file size ${nm}] == 0} {
        return ""
    }

    # Check whether we can open it at all!
    if {[catch {set busyfd [open ${nm} r]} msg]} {
        sn_error_dialog ${msg}
        return "error"
    }
    close ${busyfd}

    if {[catch {dbopen tmp_proj ${nm} RDONLY [sn_db_perms] hash}]} {
        return ""
        # It is not a database file.
    }
    set inf [tmp_proj get -key open_info]
    tmp_proj close

    if {${inf} == ""} {
        return ""
        # Nobody uses it.
    }

    sn_log "Project is used by: ${inf}"

    set interp [lindex ${inf} 0]
    set pid [lindex ${inf} 1]
    set host [lindex ${inf} 2]
    set user [lindex ${inf} 3]
    set tcp_ip_port [lindex ${inf} 4]

    if {${host} == [info hostname]} {
        # We are on the same machine.
        if {${pid} == [pid]} {
            # The current process is using the project.
            return "thisprocess"
        }
        if {$tcl_platform(platform) == "unix"} {
            set ret [sn_unix_check_process "hyper" $pid]

            if {$ret == 1} {
                return ""
            } elseif {$ret == 0} {
                # Another hyper is running, fall through to username check
            } else {
                # We only signal to see if other process is alive
                # when the user names match. This avoids a problem
                # where the kill command fails because we don't
                # have permission to signal the process.
                if {(${user} == [get_username]) &&
                        [catch {exec kill -0 ${pid} 2>/dev/null}]} {
                    return ""
                }
            }
        } else {
            if {![isfileused $nm]} {
                return ""
            }
        }
    } else {
        return "othersystem"
    }

    if {${user} == [get_username]} {
        return "thisuser"
    }
    return "thissystem"
}

# Check to see if the given pid corresponds to the given
# executable name. If we are sure it does not, 1 is returned.
# If we are sure it does, 0 is returned. If we are unsure
# then -1 is returned.

proc sn_unix_check_process { exe pid } {
    if {! [file isdirectory /proc]} {
        return -1
    }
    if {! [file isdirectory /proc/$pid]} {
        return 1
    }

    # Linux/BSD style /proc
    if {[file readable /proc/$pid/cmdline]} {
        set fd [open /proc/$pid/cmdline r]
        fconfigure $fd -translation binary -encoding binary
        set data [read $fd]
        close $fd
        set argv0 [lindex [split $data \0] 0]
        set tail [file tail $argv0]
        if {$tail == $exe} {
            sn_log "found process \"$exe\" with pid $pid"
            return 0
        } else {
            return 1
        }
    }

    # Solaris /proc
    if {[file readable /proc/$pid/psinfo]} {
        set fd [open /proc/$pid/psinfo r]
        fconfigure $fd -translation binary -encoding binary
        set data [read $fd]
        close $fd
        # Extract null terminated string at byte offset
        # 88, psinfo_t->pr_fname from <sys/procfs.h>
        set null [string first \0 $data 88]
        incr null -1
        set argv0 [string range $data 88 $null]
        if {$argv0 == $exe} {
            sn_log "found process \"$exe\" with pid $pid"
            return 0
        } else {
            return 1
        } 
    }

    return -1
}


proc sn_choose_project {{win ""} {initdir ""} {open "open"}} {
    global sn_options

    if {${win} == ""} {
        set win "."
    }
    if {${initdir} == ""} {
        set initdir $sn_options(sys,project-dir)
    }
    set nm [Editor&::FileDialog ${win} -title [get_indep String OpenProject]\
      -save_open ${open} -initialdir ${initdir}\
      -extensions $sn_options(project_extensions) -defaultextension ".proj"]

    return ${nm}
}


# open an existing project by creating a new interpreter
proc sn_open_project {{nm ""}} {
	global errorInfo tcl_platform

	set lock_error ""

	if {${nm} == ""} {
		set nm [sn_choose_project]
		if {${nm} == ""} {
			return
		}
	}

	# Lets check it whether we are allowed to open it!
	if {[catch {dbopen tmp_proj ${nm} RDONLY [sn_db_perms] hash} msg]} {
	        sn_error_dialog ${msg}
        	return
	}
	tmp_proj close

	set ret [sn_is_project_busy ${nm} in remuser remhost port pid]
    
	switch -- ${ret} {
	"othersystem" {
		set lock_error [format [get_indep String ProjAlreadyOpenedOtherSystem] \
				${remuser} ${nm} ${remhost}]
		break
	}
        
	"thisprocess" {
                set lock_error [format \
                    [get_indep String ProjAlreadyOpenedThisProcess] \
                    ${nm}]
		break
	}
        
	"thisuser" {
                set lock_error [format \
                    [get_indep String ProjAlreadyOpenedThisUser]\
                    ${nm} ${pid}]
                break
	}
        
	"thissystem" {
                set lock_error [format \
                    [get_indep String ProjAlreadyOpenedThisSystem] \
                    ${remuser} ${nm} ${pid}]
                break
	}
        
	"error" {
                return
	}
	
	}

	if {${ret} == ""} {
		# everything is fine, just go forward with startup
		# FIXME: why does this not call sn_new_project ??
		create_interp "
			wm withdraw .
			sn_start_new_session [list [list ${nm}]]
			"
	} else {
		sn_error_dialog lock_error
	}
	
}

proc sn_set_project_permission {perm} {
    global sn_options
    global tcl_platform

    if {$tcl_platform(platform) == "windows"} {
        #no permissions for windows
        return
    }

    sn_log "Setting permission: [format 0%o ${perm}]"

    #make sure we stay in project directory (different interpeters).
    catch {cd $sn_options(sys,project-dir)}

    #when command not availiable, just return
    if {[info commands paf_db_f] == ""} {
        return
    }

    # Fetch existing highlighting files!
    set fls [paf_db_f seq -col 3 -result {*[a-zA-Z0-9_]*}]
    foreach f ${fls} {
        catch {file attributes ${f} -permission ${perm}}
    }

    set pref $sn_options(db_files_prefix)
    foreach f [glob -nocomplain ${pref}.*] {
        if {[catch {file attributes ${f} -permission ${perm}} err]} {
            sn_error_dialog ${err}
            return
        }
    }

    if {[catch {file attributes $sn_options(sys,project-file)\
      -permission ${perm}} err]} {
        sn_error_dialog ${err}
        return
    }
}

proc sn_db_delete_files {projdir dbpref {project 1}} {
    global sn_options

    sn_log "delete project files of <${projdir}, ${dbpref}>"

    set cd_code [catch {cd ${projdir}}]

    if {${dbpref} == ""} {
        return 0
    }

    sn_log "file projects stored in <${dbpref}>"

    #get the symbol database directory
    set wd [file dirname ${dbpref}]

    if {${wd} == "" || ${cd_code}} {
        return 0
    }

    #database is locked?!
    if {[sn_db_panic_check ${dbpref}] == "locked"} {
        sn_error_dialog [get_indep String DatabaseLocked]
        return 0
    }

    #delete *.HIGH files (maybe this files no longer used and this line
    #can be deleted)
    if {![catch {set fls [paf_db_f seq -col 3 -result {*[a-zA-Z0-9_]*}]}]} {
        catch {eval file delete -- ${fls}}
    }

    if {!${project}} {
        #don't remove the views (needed by reparsing a project)
        if {[info commands paf_db_f] != ""} {
            set fil_list [sn_project_file_list 0]
            paf_db_proj put source_files ${fil_list}
        }

        # The *.[1-9]* files are views, don't delete them!
        set del_fls [glob -nocomplain ${dbpref}.\[A-Za-z\]*]
    } else {
        set del_fls [glob -nocomplain ${dbpref}.*]
    }

    #close all project database files now.
    db_close_files ${project}

    #delete other files that can have been created from
    #source-navigator
    set fls [glob -nocomplain [file join ${wd} *.html] [file join ${wd}\
      dbimp_*] [file join ${wd} tmp_*]]
    if {${fls} != ""} {
        catch {eval file delete -- ${fls}}
    }

    if {${del_fls} != ""} {
        set delerr [catch {eval file delete -- ${del_fls}} err]
    } else {
        set delerr 0
    }

    #The database directory might not be empty.
    catch {file delete -- ${wd}}

    if {${delerr}} {
        sn_error_dialog ${err}
        return -1
    }

    return 1
}

#close project database files. If close_proj == 0, it doesn't
#close the project and view files.
proc db_close_files {{close_proj 1}} {
    global sn_all_scopes

    set file_list [eval list by to exclude f ${sn_all_scopes}]

    if {${close_proj}} {
        lappend file_list proj
        catch {paf_db_proj delete open_info}
        # Mark it as unused!
    }
    foreach f ${file_list} {
        if {[info commands paf_db_${f}] != ""} {
            catch {paf_db_${f} sync}
            catch {paf_db_${f} close}
            sn_log -l 2 "paf_db_${f} has been closed"
        }
    }
}

proc sn_db_open_files {{show_error 1} {exit_if_db_crashed 1}} {
    global sn_options
    global errorCode errorInfo sn_debug
    global sn_all_scopes

    sn_log "open db files in <$sn_options(sys,project-dir),\
      $sn_options(db_files_prefix)>"

    set flags "cachesize=[expr $sn_options(def,db_cachesize) * 1024]"

    if {[sn_db_panic_check $sn_options(db_files_prefix)] == "died"} {
        sn_log "db process seems to have been crashed!!"
        if {${exit_if_db_crashed}} {
            set answer [tk_dialog auto [sn_title [get_indep String Error]]\
              [get_indep String DataBaseUnusable] error_image 0\
              [get_indep String Exit] [get_indep String Continue]]
            if {${answer} == 0} {
                sn_quit exit_any_way
                delete_interp
            }
            set ret 0
        } else {
            set ret -2
        }
        if {${ret} == -2} {
            return -2
        }
    }

    sn_log "open exclude <$sn_options(db_files_prefix)>"

    set exclude ""
    set empty_files ""
    if {![catch {sn_db_open_exclude}] && [info commands paf_db_exclude] != ""} {
        if {[paf_db_exclude isempty]} {
            lappend empty_files paf_db_exclude
            paf_db_exclude close
        } else {
            set exclude paf_db_exclude
        }
    }

    sn_log "exclude: <${exclude}>"

    if {[catch {dbopen paf_db_f $sn_options(db_files_prefix).f RDONLY\
      [sn_db_perms] btree ${flags} ${exclude}} msg]} {
        catch {sn_log "can't open $sn_options(db_files_prefix).f"}
    }

    set files ${sn_all_scopes}
    if {![sn_processes_running]} {
        lappend files by to
    }

    sn_log "all scopes: <${files}>"
    sn_log "create db's for all scopes"

    foreach f ${files} {

        sn_log "open database <$sn_options(db_files_prefix).${f}>"

        set file $sn_options(db_files_prefix).${f}
        if {${f} == "fil"} {
            set view ""
        } else {
            set view ${exclude}
        }
        sn_log -l 2 "DBOPEN: ${file}, exclude: ${view}"

        if {![file exists ${file}]} {
            lappend empty_files paf_db_${f}
        }\
        elseif {[catch {dbopen paf_db_${f} ${file} RDONLY [sn_db_perms] btree\
          ${flags} ${view}} msg]} {
            set e_inf ${errorInfo}
            set e_code ${errorCode}
            if {${show_error}} {
                sn_error_dialog ${msg}

                return 0
            } else {
                return -code error -errorinfo ${e_inf} -errorcode ${e_code}\
                  ${errmsg}
            }
        }\
        elseif {[paf_db_${f} isempty]} {
            lappend empty_files paf_db_${f}

            paf_db_${f} close
        }
    }

    sn_log "empty db files: [lsort ${empty_files}]"

    return 1
}

#when new flag is set, it means that the project has to be created
#if it doesn't exist
proc sn_save_project {{del_open_inf 1} {new ""}} {
    global sn_options
    global tkText sn_elix
    global sn_project_version sn_product_version

    sn_log "save project <${del_open_inf}, ${new}>"

    #create the project file if not availiable
    if {${new} != "" || ![file exists $sn_options(sys,project-file)]} {
        if {[catch {dbopen paf_db_proj $sn_options(sys,project-file)\
          {RDWR CREAT TRUNC} [sn_db_perms] hash} err]} {
            sn_error_dialog ${err}
            return 0
        }
        #save version number for project and product
        #this has to be stored only one time
        paf_db_proj put product_version ${sn_product_version}
        paf_db_proj put project_version ${sn_project_version}
        paf_db_proj put open_info [list [tk appname] [pid] [info hostname]\
          [get_username] 0]

        if {${sn_elix}} {
            # EL/IX change: initialize EL/IX variables.
            Elix&::set_defaults
        }

        paf_db_proj sync

        Preferences&::update_global_settings
    }

    if {${del_open_inf}} {
        paf_db_proj delete open_info
    }

    #save actual project settings
    Preferences&::save_project_settings ${new}

    # Write out any ide-specific entries.
    ide_write_project_preferences paf_db_proj

    paf_db_proj sync

    sn_log "save project...successfull"

    return 1
}

#make sure that the temporary files are created in the symbol db directory!!!
#this because on some OS's the temporary directory doesn't have alot of space
proc sn_set_tmp_dir {tmpdir} {
    global sn_options
    global env tcl_platform sn_root

    #the problem of disk space is UNIX-problem, so don't change
    #it on windows
    if {$tcl_platform(platform) == "windows"} {
        return
    }
    if {[file pathtype ${tmpdir}] == "relative"} {
        set tmpdir [file join $sn_options(sys,project-dir) ${tmpdir}]
    }
    set tmpdir [file nativename ${tmpdir}]

    sn_log "Project tmpdir: ${tmpdir}"

    set env(TMPDIR) ${tmpdir}
    set env(TMP) ${tmpdir}
    set env(tmp) ${tmpdir}
}

# This proc is used to undo the tmp dir setting done by
# sn_set_tmp_dir. It will unset any env vars and return
# what the tmp dir was set to or "" if it was not set.

proc sn_unset_tmp_dir {} {
    global env tcl_platform
    if {$tcl_platform(platform) == "windows"} {
        return
    }
    if {![info exists env(TMPDIR)] ||
        ![info exists env(TMP)] ||
        ![info exists env(tmp)]} {
        return
    }
    set tmp $env(TMPDIR)
    unset env(TMPDIR)
    unset env(TMP)
    unset env(tmp)
    return $tmp
}

#raise the first found symbol browser or multi window
proc sn_raise_project {} {

    foreach br [itcl::find objects "*" -class SymBr&] {
        ${br} raise
        return
    }
    foreach win [itcl::find objects "*" -class MultiWindow&] {
        ${win} raise
        return
    }
}

proc sn_upgrade_project {product_version project_version} {
    global sn_options
    global sn_project_version sn_product_version

    #it's not possible to open a newer project with an older SN version
    if {${project_version} != "" && ${project_version} >\
      ${sn_project_version}} {
        set msg [get_indep String NewerProjectCannotBeOpened]
        sn_error_dialog ${msg}
        return "break"
    }
    set msg [get_indep String OlderProjectOpened]
    set answer [tk_dialog auto [get_indep String OpenProject] ${msg}\
      question_image 0 [get_indep String Reparse] [get_indep String Cancel]]
    if {${answer} != 0} {
        return "break"
    }

    #now we want to reparse the project
    set ret [Preferences&::Reparse "donotask" "donotreopendbfiles"]

    #if reparsing was successfull, we can actualize now the version
    #number
    if {${ret} == "success"} {
        paf_db_proj put product_version ${sn_product_version}
        paf_db_proj put project_version ${sn_project_version}

        #save default settings into the project
        Preferences&::save_project_settings

        paf_db_proj sync
        paf_db_proj reopen
    }
    return ${ret}
}

proc sn_read_project {projfile} {
    global sn_options
    global env tkeWinNumber
    global sn_project_version sn_product_version
    global sn_demo
    global prj_lines_num

    # Can we open it at all ?
    if {[catch {set readfd [open ${projfile} "r"]} err]} {
        sn_error_dialog ${err}
        return 0
    }
    # Later we open it with dbopen.
    close ${readfd}

    set sn_options(sys,project-file) [realpath -pwd [pwd] ${projfile}]
    set projfile $sn_options(sys,project-file)
    set dir [file dirname ${projfile}]

    if {[catch {cd ${dir}} err]} {
        sn_error_dialog ${err}
        return 0
    }

    sn_log "Using project file: $sn_options(sys,project-file)"

    set cloned 0
    set interactive 1
    while {${interactive}} {
	
	# this code should be reworked. it's copy-pasted in sn_open_project
	# however if change something here, SN will not come up anymore so
	# I just made sure it currently works -Freek
	set nm $sn_options(sys,project-file)
        set ret [sn_is_project_busy $nm in user host port pid]
	
	switch -- ${ret} {
	"othersystem" {
		set lock_error [format [get_indep String ProjAlreadyOpenedOtherSystem] \
				${remuser} ${nm} ${remhost}]
	}
        
	"thisprocess" {
                set lock_error [format \
                    [get_indep String ProjAlreadyOpenedThisProcess] \
                    ${nm}]
	}
        
	"thisuser" {
                set lock_error [format \
                    [get_indep String ProjAlreadyOpenedThisUser]\
                    ${nm} ${pid}]
	}
        
	"thissystem" {
                set lock_error [format \
                    [get_indep String ProjAlreadyOpenedThisSystem] \
                    ${remuser} ${nm} ${pid}]
	}
        
	"error" {
                return
	}
	
	}

	if {${ret} == ""} {
    		break
	} else {
		# project is locked, do some error handling
		puts "project is locked"
		set ret [tk_dialog auto [get_indep String ExternalEditor] ${lock_error} \
				question_image 0 \
				[get_indep String ok] [get_indep String ProjForceUnlock]]
	        
		# force unlock has been chosen
		if { ${ret} == 1 } {
			sn_project_force_unlock ${nm}
		} else {
			return -1
		}
	}
    }

    if {${cloned} && [catch {file copy -force ${projfile}\
      $sn_options(sys,project-file)} err]} {
        sn_error_dialog ${err}
        return 0
    }

    #open project file
    if {[catch {dbopen paf_db_proj $sn_options(sys,project-file) RDWR\
      [sn_db_perms] hash} err]} {
        set humanerr "[format [get_indep String ErrorInvalidProjectFile]\
                       $sn_options(sys,project-file)]\n ($err)"
        sn_error_dialog $humanerr
        return 0
    }

    if {${cloned}} {
        # It is necessary because we might get into error
        # before the 'open_info' will be overwritten.
        paf_db_proj del open_info
        paf_db_proj sync
    }

    #project directory
    set sn_options(sys,project-dir) [file dirname $sn_options(sys,project-file)]

    #here we must test if we have a compatible project file,
    #if not take the stips required.
    set project_version [paf_db_proj get -key project_version]
    set product_version [paf_db_proj get -key product_version]
    if {${project_version} != ${sn_project_version}} {
        #we have a chance to reparse the project to actualize it
        #for the new project.
        set ret [sn_upgrade_project ${product_version} ${project_version}]
        if {${ret} != "success" && ${cloned}} {
            file delete -- $sn_options(sys,project-file)
        }
        if {${ret} != "success"} {
            return 0
        }
    }

    #Read project settings
    Preferences&::load_project_settings

    sn_log "Project Directory: <$sn_options(sys,project-dir)>"

    # Read IDE-specific items.
    ide_read_project_preferences paf_db_proj

    if {${cloned}} {
        paf_db_proj put OriginalProject ${projfile}

        # If the project directory was relative to the project
        # file, it has to be checked whether the cloned
        # project file's directory remaind the same than the original
        # project file's.
        if {$sn_options(sys,project-dir) == "." && [file dirname ${projfile}]\
          != [file dirname $sn_options(sys,project-file)]} {
            set sn_options(sys,project-dir) [file dirname ${projfile}]
        }

    }

    #check browser commands for correct path settings
    sn_check_browsers_path

    sn_browser_scope_menu_labels

    if {$sn_options(sys,project-dir) == "."} {
        set sn_options(sys,project-dir) [file dirname\
          $sn_options(sys,project-file)]
        maybe_ide_property set project-root $sn_options(sys,project-dir) project
    }

    sn_log "Changing to project directory: \"$sn_options(sys,project-dir)\""
    if {[catch {cd $sn_options(sys,project-dir)} err]} {
        sn_error_dialog ${err}

        #ask to delete the project!!
        sn_delete_current_project
        return 0
    }

    if {![Preferences&::create_database_dir]} {
        return 0
    }

    set save_dir [pwd]
    if {[catch {cd $sn_options(both,db-directory)} err]} {
        sn_error_dialog ${err}
        return 0
    }
    catch {cd ${save_dir}}

    #if we don't have permissions to the db directory
    #set the flag for readonly project
    if {[file isdirectory $sn_options(both,db-directory)] && ![file writable\
      $sn_options(both,db-directory)]} {
        set sn_options(readonly) yes
    }

    #make sure that the temporary files are created in the symbol db\
      directory!!!
    #this because on some OS's the temporary directory doesn't have alot of\
      space
    sn_set_tmp_dir $sn_options(both,db-directory)

    if {[catch {set ret [sn_db_open_files 0 0]}]} {
        set ret -1
    }

    if {${ret} == 0} {
        return 0
    }
    if {${ret} == -2} {
        set answer [tk_dialog auto [sn_title [get_indep String Error]]\
          [get_indep String DataBaseCrashed] error_image 0 [get_indep String\
          Reparse] [get_indep String Continue] [get_indep String Exit]]

        switch -- ${answer} {
            0 {
                    set ret -1
                }
            2 {
                    return 0
                }
        }
    }

    if {${ret} == -1 && ${interactive}} {
        # It will force sn_parse_uptodate to to reparse the whole project.
        sn_db_delete_files $sn_options(sys,project-dir)\
          $sn_options(db_files_prefix) 0
        if {![Preferences&::create_database_dir]} {
            return 0
        }
    }

    if {!${interactive}} {
        return 0
    }

    #scan files, only if the user wants to do this
    if {$sn_options(def,refresh-project)} {
        sn_loading_message [get_indep String Scanning]
        if {![sn_parse_uptodate]} {
            return 0
        }
    }

    #init rcs variables
    sn_init_rcs_variables

    #format: Interpeter PID HostName UserID PortNumber
    paf_db_proj put open_info [list [tk appname] [pid] [info hostname]\
      [get_username] 0]
    paf_db_proj sync

    sn_finalize_project open

    catch {sn_rc_project_open paf_db_proj}

    sn_log "project opened"

    return 1
}

# This function returns the name of the excluded files of a project
# concerning the current view.
proc project_excluded_file_list {} {
    if {[info commands paf_db_exclude] == ""} {
        return ""
    }
    return [paf_db_exclude seq -data]
}

proc load_files_without_parser {files} {
    global sn_options sn_sep

    sn_log "load_files_without_parser: load files without parser.."

    #make sure we stay in project directory (different interpeters).
    catch {cd $sn_options(sys,project-dir)}

    if {[catch {dbopen load_f $sn_options(db_files_prefix).f {CREAT RDWR}\
      [sn_db_perms] btree}]} {
        return
    }

    foreach lf ${files} {
        if {[catch {set mtime [file mtime ${lf}]}]} {
            #file doesn't exist
            continue
        }
        load_f put ${lf} "others${sn_sep}${mtime}"
    }
    load_f close

    sn_log "load_files_without_parser...end"
}

# Zsolt Koppany, 19-feb-1998
# This function fetches the defined symbols of files and creates
# a list that will be used later to generate keys for deleting xref
# records from the .to and .by tables.
proc sn_make_delfilelist {files} {
    global sn_options sn_sep
    if {[info commands paf_db_fil] == ""} {
        return ""
    }

    set fdd_filename [sn_tmpFileName]

    set fdd_fd [open ${fdd_filename} "w+"]
    fconfigure ${fdd_fd} \
        -encoding utf-8 \
        -blocking 0

    foreach fdel ${files} {
        puts ${fdd_fd} [join [paf_db_fil seq -uniq -col [list "2 ;" "3 ;"\
          "4 ;" "0 ;"] "${fdel}${sn_sep}"] \n]
    }
    close ${fdd_fd}
    return ${fdd_filename}
}


# This method is called with a list of files that
# should be reparsed

proc sn_parse_uptodate {{files_to_check ""} {disp_win 1}} {
    global sn_options
    global ProcessingCancelled
    global Avail_Parsers Parser_Info
    global parse_uptopdate_status

    sn_log "sn_parse_uptodate ${files_to_check} ${disp_win}"

    #make sure we stay in project directory (different interpeters).
    catch {cd $sn_options(sys,project-dir)}

    #it can be possible that this variable is not reseted
    set ProcessingCancelled 0

    #read-only project can't be changed!
    if {$sn_options(readonly)} {
        return 1
    }
    # If there is a lock, return immediately,
    # we can't write into the database.
    if {[sn_db_panic_check $sn_options(db_files_prefix) 1] != "ok"} {
        return 1
    }

    #make sure that no group is set to look for.
    set type ""
    foreach t ${Avail_Parsers} {
        if {[info exists ${t}]} {
            unset ${t}
        }
    }

    set files_to_scan 0
    set refresh 0
    set loaded_files ""
    set have_group_mtime 1
    set new_files ""

    if {${files_to_check} == ""} {
        set only_update 0

        #we must be carefull here, when the files database is not defined
        #only a list of files is returned,
        #file1 file2 file3 .....
        #Usual case is that the list has three fields:
        #{file group mtime} .....
        set files_to_add [sn_project_file_list]
        if {[info commands paf_db_f] == ""} {
            set have_group_mtime 0
        }

    } else {
        set only_update 1
        set p_files ""
        foreach f ${files_to_check} {
            set f [sn_convert_FileName ${f}]

            # The next insruction is a bit tricky:
            # If a view is active and the file could
            # be removed from the view, the browser
            # windows must be refreshed.
            if {[info commands paf_db_exclude] != ""} {
                set err [catch {set refr [paf_db_exclude del ${f}]}]
            } else {
                set err 0
                set refr 0
            }
            if {!${err} && ${refr}} {
                set refresh 1
            }

            #maybe the first file in the project, that means that
            #paf_db_f doesn't exists
            if {[info commands paf_db_f] != ""} {
                set pars [paf_db_f get -col {0 1 2} ${f}]
            } else {
                set pars ""
            }

            if {${pars} == ""} {
                set type [sn_get_file_type ${f}]
                if {${type} == ""} {
                    sn_log "No language type for \"${f}\", use default <others>"
                    set type "others"
                }
                lappend new_files ${f}
                set pars [list ${f} ${type} ""]
            }
            lappend p_files ${pars}
        }
        set files_to_add ${p_files}
    }

    set files_changed ""
    set files_without_parser ""
    set files_not_found ""
    set type ""
    set mtime ""
    set more 0
    foreach f ${files_to_add} {
        if {${more}} {
            update
        } else {
            set more 1
        }
        if {${have_group_mtime}} {
            set name [lindex ${f} 0]
            set type [lindex ${f} 1]
            set mtime [lindex ${f} 2]
        } else {
            set name ${f}
            set type ""
            set mtime ""
        }

        sn_log -l 2 "name:${name},type:${type},mtime:${mtime}"
        flush stdout

        if {![file exists ${name}]} {
            lappend files_not_found ${name}
            continue
        }
        if {[file isdirectory ${name}] || [file mtime ${name}] == ${mtime}} {
            continue
        }

        sn_log "Reparsing: ${name}, mtime: ${mtime}, file mtime: [file mtime\
          ${name}]"

        if {${type} == ""} {
            set type [sn_get_file_type ${name}]
        }

        lappend files_changed ${name}

        set brow_exec $Parser_Info(${type},BROW)
        if {${brow_exec} != ""} {
            lappend grouped_files(${type}) ${name}
            incr files_to_scan
        } else {
            lappend files_without_parser ${name}
        }
    }

    # Now we can delete files from the project that don't exists any more!
    if {${files_not_found} != ""} {
        set w [sourcenav::Window .not_found_files]

        ${w} configure -title [get_indep String FileNotExist]

        sn_motif_buttons ${w} bottom 0 [get_indep String Hide]\
          [get_indep String Delete] [get_indep String Continue]\
          [get_indep String cancel]

        set parse_uptopdate_status ""

        ${w}.button_0 config -command { set parse_uptopdate_status "hide" }
        ${w}.button_1 config -command { set parse_uptopdate_status "unload" }
        ${w}.button_2 config -command { set parse_uptopdate_status "continue" }
        ${w}.button_3 config -command { set parse_uptopdate_status "cancel" }

        set len [llength ${files_not_found}]
        if {${len} > 20} {
            set len 20
        }\
        elseif {${len} < 5} {
            set len 5
        }

        #add some comments to the not found files
        label ${w}.notfound -text [get_indep String FilesNotFound] -anchor nw\
          -justify left
        pack ${w}.notfound -side top -fill x

        # do the listbox, incl scrollbars
        set files_not_found [lsort -dictionary ${files_not_found}]
        listbox ${w}.sel -height ${len} -width 40 -yscroll "${w}.yscr set" -xscroll "${w}.xscr set"
	scrollbar ${w}.xscr -orient horizontal -command "${w}.sel xview"
	scrollbar ${w}.yscr -orient vertical -command "${w}.sel yview"
        eval ${w}.sel insert 0 ${files_not_found}

        # FIXME: we need to find a better way to do this or else
        # rethink the removal of this widgets bind command
	bind ${w}.sel <Return> "${w}.button_0 invoke"
	bind ${w}.sel <Escape> "${w}.button_2 invoke"
	bind ${w}.sel <Button-4> "${w}.sel yview scroll -5 units"
	bind ${w}.sel <Button-5> "${w}.sel yview scroll 5 units"
	
	pack ${w}.yscr -side right -fill y
	pack ${w}.xscr -side bottom -fill x
        pack ${w}.sel -side left -expand y -fill both
	
        ${w} move_to_mouse
        ${w} raise
        ${w} grab set

        hide_loading_message

        vwait parse_uptopdate_status

        itcl::delete object ${w}

        switch ${parse_uptopdate_status} {
            "cancel" {
                    return 0
                }
            "unload" {
                    sn_load_hide_unload_files "" "" "" ${files_not_found}
                    set refresh 1
                }
            "hide" {
                    sn_load_hide_unload_files "" "" ${files_not_found}
                    set refresh 1
                }
        }
    }

    # Files that have been changed must be deleted from the view.
    if {[info commands paf_db_exclude] != ""} {
        foreach f ${files_changed} {
            catch {paf_db_exclude del ${f}}
        }
        paf_db_exclude sync
    }

    set fdd_filename ""
    if {${files_to_scan} > 0 || ${refresh}} {
        if {${files_to_scan} > 0} {
            set fdd_filename [sn_make_delfilelist ${files_changed}]
        }
        # The database files will be changed by a parser.
        db_close_files 0
    }

    #add not language files to the project.
    if {${files_without_parser} != ""} {
        load_files_without_parser ${files_without_parser}
    }

    if {${files_to_scan} > 0} {
        set xfer_file [sn_tmpFileName]
        set parsefd [open ${xfer_file} "w+"]
        fconfigure ${parsefd} \
            -encoding utf-8 \
            -blocking 0

        sn_log "file symbols for delete are in: ${fdd_filename}"
        puts ${parsefd} "-3;$sn_options(db_del_type);${fdd_filename}"
        close ${parsefd}
        set xfer_size [file size ${xfer_file}]

        #make the status window optional
        if {${disp_win}} {
            set scale_window [make_scale_window ${files_to_scan} 1]
        } else {
            set scale_window "never_exists"
        }

        set cbrowser_xref 0

        #scan language files grouped by there own language
        #type (c++, tcl, java, ....)
        foreach type [array names grouped_files] {
            set brow_exec $Parser_Info(${type},BROW)
            set brow_swi $Parser_Info(${type},BROW_SWITCH)

            if {${brow_exec} == ""} {
                continue
            }
	    
	    # If parsing c/c++ file with cbrowser, pass
            # a flag to sn_load_xref so that it knows to
            # use cbrowser2 in the xref gen stage.
            if {[string first cbrowser $brow_exec] != -1} {
                set cbrowser_xref 1
            }
	    
            set lfiles [lsort -dictionary $grouped_files(${type})]

            set brow_cmd [list ${brow_exec}]
            if {${brow_swi} != ""} {
                eval lappend brow_cmd ${brow_swi}
            }

            #append macro files to the parser
            set macroflag $Parser_Info(${type},MACRO)
            if {${macroflag} == "-m"} {
                foreach mf $sn_options(macrofiles) {
                    lappend brow_cmd -m ${mf}
                }
            }

            # '-t' means drop /... files.
            lappend brow_cmd -t

            #we must verify the exactly number of files to generate
            #xref-information for.
            if {[lsearch -exact $sn_options(sys,language-with-xref) ${type}]\
              != -1} {
                xref_termometer_files + ${lfiles}
            }

            if {![sn_load_files ${brow_cmd} ${lfiles} ${xfer_file}\
              ${scale_window}] || ${ProcessingCancelled}} {
                break
            }
        }
        delete_scale_window ${scale_window}
    }

    if {${files_to_scan} > 0 || ${refresh}} {
        if {[sn_db_open_files] != 1} {
            return 0
        }
    }

    # Reload the files being edited!
    if {${files_to_check} == ""} {
        set files_to_check ${files_changed}
    }

    #add new files into the file list
    if {${new_files} != "" && [info commands paf_db_proj] != ""} {
        set prj_files [paf_db_proj get -key source_files]
        eval lappend prj_files ${new_files}
        #make sure that the file list is unique
        set prj_files [lunique ${prj_files}]
        paf_db_proj put source_files ${prj_files}
        maybe_ide_property set project-files ${prj_files} project
    }

    #start xref generation
    if {${files_to_scan} > 0} {
        if {[file size ${xfer_file}] == ${xfer_size}} {
            file delete -- ${xfer_file}
        } else {
            sn_load_xref ${xfer_file} ${cbrowser_xref}
        }
    }

    ## Refresh existing views (Editor, Class, XRef, ....)
    if {${refresh} || ${files_to_scan} > 0} {
        SN_Refresh_Windows
    }

    return 1
}

#txt: text, that should be added to the title prefix
#new: if the title should be created for a window in
#     creating a new project
proc sn_title {{txt ""} {new 0}} {
    global sn_options
    global tcl_platform

    if {$tcl_platform(platform) == "windows"} {
        if {${txt} != ""} {
            set txt "${txt} -"
        }
        if {${new}} {
            return [string trim "${txt} [get_indep String ProductName]"]
        } else {
            return [string trim "${txt} [get_indep String ProductName]\
              \[$sn_options(sys,project-name)\]"]
        }
    } else {
        if {${new}} {
            return [string trim "[get_indep String ProductName] ${txt}"]
        } else {
            return [string trim "[get_indep String ProductName]\
              \[$sn_options(sys,project-name)\] ${txt}"]
        }
    }
}

proc sn_view_title {title {txt ""}} {
}

proc sn_view_icon {title {txt ""}} {
    global sn_options
    if {${title} != ""} {
        return [string trim "${title}: ${txt}\
          \[$sn_options(sys,project-name)\]"]
    } else {
        return [string trim "${title} ${txt} \[$sn_options(sys,project-name)\]"]
    }
}

# This method is called when a fileevent on a subprocess pipe is ready. For
# instance the parser might have finished processing a file.

proc event_LoadPipeInput {eventfd sc} {
    global ProcessingCancelled
    global PafLoadPipeEnd
    global event_LoadPipeInput_last_accessed_file
    global sn_debug

    #the process has been canceled by the user
    if {${ProcessingCancelled} == 2} {
        sn_log "event_LoadPipeInput : processing was canceled by the user"
        fileevent ${eventfd} readable {}
        set PafLoadPipeEnd 1
	return
    }

    if {[catch {set eof [eof ${eventfd}]} err]} {
        set error 1
    } else {
        set error 0
    }

    #the parser crashed
    if {${error} || ${ProcessingCancelled} || ${eof}} {

# FIXME : Tcl fails to raise an error
# during a call to the close command
# when the program on the other end
# of a non-blocking pipe crashes.
# We work around this by putting the
# pipe back in blocking mode.
        fconfigure ${eventfd} -blocking 1
        set ret [catch {close ${eventfd}} err]
        if {${ret} && !${ProcessingCancelled}} {
            sn_log "event_LoadPipeInput close : error ${err}"

            # If the parser crashed, then we should show
            # an error message to the user instead of
            # just continuing.

            if {[string match "*child killed*" $err] ||
                    [string match "*abnormal program termination*" $err] ||
                    [string match "*child process exited abnormally*" $err]} {
                set crashed 1
            } else {
                set crashed 0
            }

            if {$sn_debug || $crashed} {
                sn_error_dialog ${err}
            }

            if {!$crashed} {
                # Setting ProcessingCancelled to 3 indicates that
                # the parsing process should continue. The next
                # block is skipped and the user is not asked if
                # they want to continue parsing.
                set ProcessingCancelled 3
            }
        }

        #ask the user to continue
        if {(${ret} || ${error}) && !${ProcessingCancelled}} {
            sn_log "event_LoadPipeInput : parser crashed or wrote \
                    to stderr, ProcessingCancelled is $ProcessingCancelled"
            set ProcessingCancelled 1
            sn_handle_parse_error
        }
        set PafLoadPipeEnd 1
        return
    }

    if {[catch {set line [gets ${eventfd}]}]} {
        return
    }
    
    if {[string equal $line ""]} {
        return
    }

    sn_log -l 2 "Info from pipe: ${line}"

    #status lines, ignore them
    set scanning "Status: Scanning: "
    set deleting "Status: Deleting: "
    if {[string first $deleting ${line}] == 0 ||
            [string first $scanning ${line}] == 0} {
        return
    }

    if {[string first "Error:" ${line}] != -1} {
        sn_log "Error from pipe: ${line}"

        set ev_fnc [fileevent ${eventfd} readable]
        fileevent ${eventfd} readable {}

        sn_error_dialog ${line}

        catch {fileevent ${eventfd} readable ${ev_fnc}}
    } else {
        # Check for parse status message from pipe,
	# format is "Status: Parsing: filename"
	set header "Status: Parsing: "
	if {[string first $header $line] == 0} {
            set fname [string range $line [string length $header] end]
            set event_LoadPipeInput_last_accessed_file ${fname}
            display_scale_window ${sc} ${fname}
	    update idletasks
	} else {
	    set ev_fnc [fileevent ${eventfd} readable]
	    fileevent ${eventfd} readable {}
	    sn_error_dialog "unrecognized pipe input \"${line}\""
	    catch {fileevent ${eventfd} readable ${ev_fnc}}
	}
    }
}

#ask to cancel parsing files to a language parser
proc sn_ask_continue_other_types {} {
    global ProcessingCancelled
    #user canceled the process
    set res [tk_dialog auto [get_indep String Processing] [get_indep String\
      ContinueParsingOtherTypes] question_image 0 [get_indep String Continue]\
      [get_indep String Stop]]
    if {${res} == 0} {
        set ProcessingCancelled 0
        return 1
    }
    return 0
}

#ask to continue parsing
proc sn_ask_continue_parsing {} {
    global ProcessingCancelled
    set res [tk_dialog auto [get_indep String Parsing] [get_indep String\
      ContinueParsing] question_image 0 [get_indep String Continue]\
      [get_indep String Stop]]
    if {${res} == 0} {
        set ProcessingCancelled 0
        return 1
    }
    return 0
}

# This method should be used by functions outside of misc.tcl (or projmng.tcl, GROAN)
# to tell the system that the processing has been canceled in some way.
# The reason variable should be set to:
# 0 -> Unknown error ?
# 1 -> Ask the user if processing should continue
# 2 -> Users decided to cancel the operation
# 3 -> The parser crashed

proc sn_processing_canceled { reason } {
    global ProcessingCancelled PafLoadPipeEnd
    sn_log "sn_processing_canceled $reason invoked"
    set ProcessingCancelled $reason
    
    # FIXME: This is a bit of a hack, but we have code that does
    # a vwait on this var so we need to set it.
    set PafLoadPipeEnd 1
}

#Error has been detected, 
#ProcessingCancelled ==:
#1. Parser error
#2. Use has canceled the parsing process
proc sn_handle_parse_error {} {
    global ProcessingCancelled
    global event_LoadPipeInput_last_accessed_file

    if {![info exists event_LoadPipeInput_last_accessed_file]} {
        set event_LoadPipeInput_last_accessed_file "<unknown>"
    }
    switch -- ${ProcessingCancelled} {
        "2" {
                # If ProcessingCancelled is set to 2, it means
                # the user canceled the parse. If that is the
		# case then sn_handle_parse_error should not get called
		error "sn_handle_parse_error after user cancel"
            }
        "0" {
                #no (unknown) error
                return 1
            }
        default {
                #error occured by the parser, ask to continue parsing
                #by skipping the last file
                set res [tk_dialog auto [get_indep String Scanning]\
                  "${event_LoadPipeInput_last_accessed_file}:\n[get_indep\
                  String ErrorByScanning]" question_image 0 [get_indep String\
                  Continue] [get_indep String Stop]]
                if {${res} == 0} {
                    #continue
                    set ProcessingCancelled 3
                    return 1
                }
            }
    }
    return 0
}

proc sn_load_files {cmd files xfer_file {sc "never_exists"}} {
    global sn_options
    global ProcessingCancelled
    global event_LoadPipeInput_last_accessed_file

    if {[sn_processes_running 1]} {
        return 0
    }

    #scan cople of files each time.
    set sp_size $sn_options(Scann_Files_At_Once)

    if {[string first "fbrowser" ${cmd}] == -1} {
        set sp_size [expr ${sp_size} * 10]
    }

    sn_log "File splitting size: ${sp_size}"

    set len [llength ${files}]
    for {set i 0} {${i} < ${len}} {incr i ${sp_size}} {
        set fl [lrange ${files} ${i} [expr ${i} + ${sp_size} - 1]]

        while {1} {
            set ret [sn_load_part_files ${cmd} ${fl} ${xfer_file} ${sc}]

            if {${ret} == 0 || ${ProcessingCancelled}} {
                #try to continue parsing by excluding the file that has
                #occured the parsing crash
                if {${ProcessingCancelled} == 3} {
                    set ProcessingCancelled 0
                    set idx [lsearch -exact ${fl}\
                      ${event_LoadPipeInput_last_accessed_file}]
                    if {${idx} != -1} {
                        set fl [lrange ${fl} [expr ${idx} + 1] end]
                    } else {
                        set fl ""
                    }
                    if {${fl} != ""} {
                        #continue parsing with the rest of files
                        continue
                    }
                } else {
                    #ask to continue parsing other language files
                    return [sn_ask_continue_other_types]
                }
            }
            break
        }
    }
    return 1
}

proc sn_load_part_files {cmd files xfer_file {sc "never_exists"}} {
    global sn_options
    global PafLoadPipeEnd sn_debug sn_path
    global ProcessingCancelled tcl_platform

    #make sure we stay in project directory (different interpeters).
    catch {cd $sn_options(sys,project-dir)}

    if {[catch {
        set coll [sn_tmpFileName]
        set collfd [open ${coll} "w+"]
        fconfigure ${collfd} \
            -encoding $sn_options(def,system-encoding) \
            -blocking 0
        puts ${collfd} [join ${files} "\n"]
        close ${collfd}

        #create a file with all the include files that are defined
        #in the project
        if {$sn_options(def,include-locatefiles)} {
            set incl [sn_tmpFileName]
            set incfd [open ${incl} "w+"]
            fconfigure ${incfd} \
                -encoding $sn_options(def,system-encoding) \
                -blocking 0
            puts ${incfd} [join $sn_options(include-source-directories) "\n"]
            close ${incfd}
        } else {
            set incl ""
        }
    } err]} {
        sn_error_dialog ${err}
        return 0
    }

    if {$sn_options(db_files_prefix) == ""} {
        sn_error_dialog "Internal error: empty database directory!"
        return 0
    }
    if {$tcl_platform(platform) == "windows"} {
        set pipe [file join $sn_path(parserdir) dbimp.exe]
    } else {
        set pipe [file join $sn_path(parserdir) dbimp]
    }

    #verify if Xref generation will be started
    set off [lsearch -exact $sn_options(sys,parser_switches) "-x"]
    if {${off} != -1} {
        set swi [linsert $sn_options(sys,parser_switches) [expr ${off} + 1]\
          ${xfer_file}]
    } else {
        set swi $sn_options(sys,parser_switches)
    }
    # The -n option is passed to cbrowser and cbrowser2 only
    if {[string first cbrowser $cmd] != -1} {
        lappend cmd -n $sn_options(db_files_prefix)
    }
    lappend cmd -y ${coll}
    if {${swi} != ""} {
        eval lappend cmd ${swi}
    }

    #look for the location of included headers
    if {$sn_options(def,include-locatefiles)} {
        lappend cmd -I ${incl}
    }

    # Character set encoding (if non-default), used by a parser when
    # reading a source file.
    if {[string compare $sn_options(def,encoding) "iso8859-1"] != 0} {
        lappend cmd -e $sn_options(def,encoding)
    }

    lappend cmd | ${pipe}
    lappend cmd -c $sn_options(def,db_cachesize) -H [info hostname] -P [pid]
    lappend cmd $sn_options(db_files_prefix)

    sn_log "Launching parser with command: ${cmd}"

    if {${sn_debug}} {
        sn_log "Include dir list: ${incl}"
        sn_log "Input file list: ${coll}"
        sn_log "Cross-ref file list: ${xfer_file}"
    }

    if {[catch {set cmdfd [open "| ${cmd}" r]} err]} {

        if {!${sn_debug}} {
            catch {file delete -- ${coll} ${incl}}
        }
        #unable to start parser, ask to continue
        sn_error_dialog "${cmd}:\n[get_indep String UnableToExecuteParser]"
        return [sn_ask_continue_parsing]
    }

    # Pipe must be non-blocking or GUI will block on cancel of parse.
    # Pipe must be in utf-8 mode since output of parsers could
    # include data that can not be represented in the system encoding.

    fconfigure ${cmdfd} \
        -encoding utf-8 \
        -blocking 0
    fileevent ${cmdfd} readable [list event_LoadPipeInput ${cmdfd} ${sc}]
    set pids [pid ${cmdfd}]

    sn_log "Waiting for PafLoadPipeEnd variable in sn_load_part_files : fd is ${cmdfd}"
    vwait PafLoadPipeEnd
    sn_log "Done waiting for PafLoadPipeEnd variable in sn_load_part_files : fd is ${cmdfd}"
    
    # FIXME: this is a hack and it will only work under UNIX, but oh well.
    # we really need to add a kill command to Tcl.
    # Test whether processes in the pipe are still alive
    if {$tcl_platform(platform) == "unix" &&
	    ![catch {eval exec kill -0 ${pids}}]} {
	sn_log "sn_load_part_files: killing pids ${pids}"
        eval exec kill -9 $pids
	update
    }

    if {[catch {close ${cmdfd}} err]} {
	sn_log "sn_load_part_files catch : $err"
    }

    catch {unset PafLoadPipeEnd}

    if {!${sn_debug}} {
        catch {file delete -- ${coll} ${incl}}
    }

    #maybe the browser has been crashed, this is true when
    #ProcessingCancelled eq 3
    if {${ProcessingCancelled} == 3} {
        return 1
    }

    if {[sn_db_panic_check $sn_options(db_files_prefix)] == "died"} {
        return 0
    }
    return 1
}

# This function checks the state of the database lock file.
# Return values:
#	died:	The locker process has disappeared/died.
#	ok:	No locking is active.
#	locked:	Active lock.
proc sn_db_panic_check {symdir {check_only_file 0}} {
    global sn_options tcl_platform
    global SN_cross_pid
    global errorCode

    #make sure we stay in project directory (different interpeters).
    catch {cd $sn_options(sys,project-dir)}

    set lck_file ${symdir}.lck
    set lck_exists [file exists ${lck_file}]

    if {! ${lck_exists}} {
        return "ok"
    }
    if {[catch {set panicfd [open ${lck_file}]} err]} {
        return "ok"
    }

    for {set i 0} {${i} < 2} {incr i} {
        set info [gets ${panicfd}]
        if {[llength ${info}] == 3} {
            break
        }
        # Somebody might write the file right now.
        after 500
        seek ${panicfd} 0 start
    }
    close ${panicfd}

    sn_log "check running processes info <${info}>"

    if {[llength ${info}] != 3 && ${lck_exists}} {
        sn_log "an empty lock file exists info <${info}>"
        return "died"
        # This should never happen.
    }

    if {${check_only_file}} {
        return "locked"
    }

    set mother_pid [lindex ${info} 0]
    set host [lindex ${info} 1]
    set pid [lindex ${info} 2]
    if {${pid} == 0} {
        return "died"
        # The project is already marked as unusabel.
    }
    if {[catch {set xref_pid [pid ${SN_cross_pid}]}]} {
        set xref_pid 0
    }
    # Are we on the same host ?
    if {${host} == [info hostname]} {
        # Check, whether we are the mother of the process!
        if {${mother_pid} == [pid]} {
            # We are the mother, the process belongs to us.
            if {${pid} == ${xref_pid}} {
                return "locked"
                # A new dbimp is running started by us.
            }
        }

# FIXME: I think this kill test is wrong, the
# true block would only get run it the pid was dead.
        # Test whether the PID is still valid.
        if {$tcl_platform(platform) == "unix" &&
	        [catch {exec kill -0 ${pid}}]} {
            # Does the lock file still exist?
            if {!${lck_exists}} {
                # In between, is has been deleted.
                return "ok"
            }
            # No, the locker process has jusr disappeared (crashed)!
            return "died"
        }

        # Don't use the network!
        set host $sn_options(def,localhost)
    }

    # It is still accessible.
    return "locked"
}

proc sn_truncate_path {path_name file_name} {
    set path_components [file split ${path_name}]
    set file_components [file split ${file_name}]
    set length [llength ${path_components}]
    if {[sn_filecmd isequal ${path_components} [lrange ${file_components} 0\
      [expr ${length} - 1]]]} {
        if {[catch {set path [eval file join [lrange ${file_components}\
          ${length} end]]}]} {
            return ${file_name}
        }
    } else {
        set path ${file_name}
        #delete a prefix of "./"
        while {[lindex ${file_components} 0] == "."} {
            set file_components [lrange ${file_components} 1 end]
            set path [eval file join ${file_components}]
        }
    }
    return ${path}
}

proc sn_update_scope_list {{refresh 1}} {
    global sn_all_scopes
    global sn_scopes
    global sn_other_scopes
    global sn_general_scopes

    set mod 0
    foreach sc ${sn_all_scopes} {
        set i [lsearch -exact ${sn_scopes} ${sc}]
        set j [lsearch -exact ${sn_other_scopes} ${sc}]
        set k [lsearch -exact ${sn_general_scopes} ${sc}]

        #scope is not shown
        if {${j} != -1} {
            continue
        }

        if {[info commands paf_db_${sc}] != "" && ${i} == -1} {
            #scope is new availiable (add it to the scope list)
            lappend sn_scopes ${sc}
            set mod 1
        }
    }

    if {${refresh} && ${mod}} {
        set sn_scopes [lsort ${sn_scopes}]
    }

    #Figure out which symbol menus should be used in the editor!
    Editor&::combobox_scopes
}


# forcefully unlock the .proj file
# contributed by Mark Thornber
proc sn_project_force_unlock {proj_file} {
	sn_log "forcing unlock of project ${proj_file}"
	
	# Check whether we can open it at all!
	#if {[catch {set busyfd [open $proj_file r]} msg]} {
	#	error "$proj_file can't be opened"
 	#}
	#close ${busyfd}

	if {[catch {dbopen proj ${proj_file} RDWR [sn_db_perms] hash}]} {
		error "$proj_file can't be opened as hash table"
	}

	set inf [proj get -key open_info]

	if {$inf == ""} {
		puts "${proj_file} is not locked"
		return -1
	}

	proj put open_info ""
	proj close

	sn_log "forcing unlock of project ${proj_file} was successful"
}

